Sequal to TryToDiagnoseDisagg.Rmd Which didn’t work the first time and kind of became a mess. And FluxInterceptSillyness.Rmd
24 March 2019
Start with core parts of the FluxInterceptSillyness solution
Then lets run dissaggregation on the flux profile.
Making a synthetic data set.
Data to make binned_EachSize_ald.csv come from Normalize_UVP_Flux.Rmd Which in turn pulls data generated by UVP2017.Rmd
options(readr.default_locale=readr::locale(tz="Mexico/General"))
source("UVP_2017_library.R")
#dataP2 <- bring_in_p2()
#twin01 <- make_twin_df_list(dataP2)
#ES01 <- twin01[[1]] %>% filter(depth <= 1000, profile == "stn_043")
#DS01 <- twin01[[2]] %>% filter(depth <= 1000, profile == "stn_043")
# # Modification, merge all stations and see what happens.
# ES01 <- twin01[[1]] #%>% filter(depth <= 1000)
# DS01 <- twin01[[2]] #%>% filter(depth <= 1000)
ES01 <- read_csv("dataOut/binned_EachSize.csv") %>% filter(depth <= 1000)
DS01 <- read_csv("dataOut/binned_DepthSummary.csv") %>% filter(depth <= 1000)
twinS <- list(ES01, DS01)
SimpleBins <- seq(from = 0, to = 1100, by = 100)
#debug(bin_depths)
binnedS <- bin_depths(twinS, bins = SimpleBins) %>% calc_psd
ESS <- binnedS[[1]]
DSS <- binnedS[[2]]
Different direction
Units of nparticles
# mySpec <- c(3.32405678509316, 1.85877856561554, 1.03359305917877, 0.564880885240349,
# 0.309079523670959, 0.165424066706246, 0.0925270232480783, 0.0502491579183327,
# 0.0275765808581977, 0.0147327962322043, 0.00847342228148116,
# 0.00457079526918403, 0.00242657514481869, 0.00133840064588931,
# 0.00073768231262027, 0.000406688319239427, 0.00021922892226164,
# 0.000120831754575073, 6.6223357185171e-05, 3.58979192483917e-05,
# 2.00799713729794e-05, 1.09115166969355e-05, 5.82645914172791e-06,
# 3.28908275460212e-06, 1.57702715137e-06)
lb_vec <- c(0.102, 0.128, 0.161, 0.203, 0.256, 0.323, 0.406, 0.512, 0.645,
0.813, 1.02, 1.29, 1.63, 2.05, 2.58, 3.25, 4.1, 5.16, 6.5, 8.19,
10.3, 13, 16.4, 20.6, 26)
binsize_vec <- c(0.026, 0.033, 0.042, 0.053, 0.067, 0.083, 0.106, 0.133, 0.168,
0.207, 0.27, 0.34, 0.42, 0.53, 0.67, 0.85, 1.06, 1.34, 1.69,
2.11, 2.7, 3.4, 4.2, 5.4, 6)
# C_f = 4
#ag = 0.26 # as in rest of universe
C_f = 10.51 # for now
alpha = .52
gamma = .26
ag = alpha + gamma
C_f_global <- C_f
alpha_global <- alpha
gamma_global <- gamma
ag_global <- ag
# mySpec
# myDf <- tibble(lb_vec, mySpec, binsize_vec)
make_spectrum <- function(icp, psd, bins = lb_vec, binsizes = binsize_vec){
C_n = exp(icp)
nnp = (C_n * bins ^ psd) * (binsizes)
nnp
}
testSpectrum <- make_spectrum( -2, -3.5)
testSpectrum
[1] 1.038207e+01 5.952372e+00 3.394453e+00 1.903053e+00 1.068184e+00 5.865166e-01 3.364153e-01 1.874208e-01 1.055022e-01 5.781819e-02
[11] 3.409371e-02 1.887235e-02 1.028025e-02 5.814982e-03 3.287130e-03 1.858824e-03 1.027953e-03 5.810880e-04 3.266638e-04 1.816351e-04
[21] 1.041944e-04 5.808824e-05 3.182049e-05 1.841915e-05 9.060571e-06
Post for Stack overflow, at least in thory
library(tidyverse)
library(cowplot)
********************************************************
Note: As of version 1.0.0, cowplot does not change the
default ggplot2 theme anymore. To recover the previous
behavior, execute:
theme_set(theme_cowplot())
********************************************************
particle_data <- ESS %>% select(-nparticles, -n_nparticles, -time, -binsize)
I have a dataset in which I have measured particles of different sizes at different depths. Each depth has several size bins associated with it lb: lower bound of particle size (mm) ub: upper bound of particle size (mm) vol: amount of water sampled per depth (l) TotalParticles: The number of particles seen in that volume (#) depth: The depth we are sampling (m)
We process the data slightly
particle_data_processed <- particle_data %>%
mutate(binsize = ub-lb, # size of particle bins
nparticles = TotalParticles/vol, # particles normalized to volume (#/L)
n_nparticles = nparticles/binsize, # particles normalized to volume and bin size (#/L/mm)
)
particle_data_processed
I model the relationship between particles and depth as a power law function. That is the log of the particle size is linearly related to the log of the size and volume normalized particle numbers. To account for zeros, I use a poisson glm
myGlm <- function(df){
glm(TotalParticles ~ log(lb), offset = log(vol * binsize), family = poisson, data = df)
}
particle_icp_psd <- particle_data_processed %>% nest(-depth, -profile) %>%
mutate(model = map(data, myGlm)) %>%
mutate(tidied = map(model, tidy)) %>%
unnest(tidied) %>%
select(depth, profile, term, estimate) %>%
spread(key = term, value = estimate) %>%
rename(icp = `(Intercept)`, psd = `log(lb)`)
All elements of `...` must be named.
Did you want `data = c(lb, ub, vol, TotalParticles, binsize, nparticles, n_nparticles)`?
particle_icp_psd
One more thing. I also care about the particle flux. Flux is the sum of all of the particles times their sinking speed, times their mass. I relate flux to mass as follows Flux{in a bin of diamter D} = C_f * D ^ ag Cf and and ag are constants Cf = 4, ag = 0.23 And Total Flux = sum_D{Flux(D)} Flux is calculated based on the particles normalized to volume
#C_f = 4
#ag = 0.23
particle_flux <- particle_data_processed %>%
mutate(flux = (C_f * nparticles ^ ag)) %>%
group_by(depth, profile) %>%
summarize(Flux = sum(flux))
particle_flux
I combine the flux data with the PSD and intercept data
particle_ipf <- left_join(particle_icp_psd, particle_flux, by = "depth")
particle_ipf
pFlux <- ggplot(particle_ipf, aes(x = Flux, y = depth)) + scale_y_reverse() + geom_point()
pPSD <- ggplot(particle_ipf, aes(x = psd, y = depth)) + scale_y_reverse() + geom_point()
picp <- ggplot(particle_ipf, aes(x = icp, y = depth)) + scale_y_reverse() + geom_point()
plot_grid(pFlux, pPSD, picp, nrow = 1)

Now, for “reasons”, I want to smooth these profiles out and interpolate some new spectra. First, I use gams to model the three profiles.
gamPSD <- gam(psd ~ s(depth), data = particle_ipf)
gamicp <- gam(icp ~ s(depth), data = particle_ipf)
gamFlux <- gam(Flux ~ s(depth), family = "Gamma", data = particle_ipf)
predData <- tibble(depth = seq(from = 0, to = 1000, by = 25))
predPSD <- predict(gamPSD, predData, type = "response")
predicp <- predict(gamicp, predData, type = "response")
predFlux <- predict(gamFlux, predData, type = "response")
predDf <- tibble(depth = predData$depth, psd = predPSD, icp = predicp, Flux = predFlux)
predDf
pFlux <- ggplot(predDf, aes(x = Flux, y = depth)) + scale_y_reverse() + geom_point() + scale_x_continuous(limits = c(0, 200))
pPSD <- ggplot(predDf, aes(x = psd, y = depth)) + scale_y_reverse() + geom_point()
pInt <- ggplot(predDf, aes(x = icp, y = depth)) + scale_y_reverse() + geom_point()
plot_grid(pFlux, pPSD, pInt, nrow = 1)
Removed 3 rows containing missing values (geom_point).

So next, I’m going to recreate spectra from psd and int. I will see if these recreated spectra approximate the observed “Flux” values.
Recall that psd and int relate to binsize and volume normalized particle numbers.
lb_vec = particle_data %>% pull(lb) %>% unique # the particle sizes
binsize_vec = particle_data_processed %>% pull(binsize) %>% unique # the particle sizes
make_spectrum <- function(icp, psd, bins = lb_vec, binsizes = binsize_vec){
C_n = exp(icp)
nnp = (C_n * bins ^ psd) * (binsizes)
nnp
}
make_spectrum <- function(icp, psd, bins = lb_vec, binsizes = binsize_vec){
nnp = exp(log(bins) * psd + icp)
np = nnp * binsizes
tibble(lb = bins, np)
}
predDf <- predDf %>% mutate(spec = map2(icp, psd, make_spectrum))
predDf[["spec"]][[5]] %>% mutate(flux = (C_f * np ^ag)) %>% summarize(Flux = sum(flux))
Now, I re-calculate flux from spec.
sumflux <- function(df){
df %>% pull(flux) %>% sum
}
predDf2 <- predDf %>%
mutate(spec = map(spec,
. %>% mutate(flux = (C_f * np ^ ag))
)) %>%
#mutate(Flux = map(spec, ~ . %>% summarise(Flux = sum(flux))))
mutate(Flux = map_dbl(spec, sumflux))
pPredFlux <- ggplot(data = predDf2, aes(y = depth, x = Flux)) + geom_point() + scale_y_reverse() + scale_x_continuous(limits = c(0, 200))
plot_grid(pFlux, pPredFlux)
Removed 3 rows containing missing values (geom_point).Removed 4 rows containing missing values (geom_point).

Unnest PredDf
predDf3 <- predDf2 %>% unnest(spec)
New names:
* `111` -> `111...11`
* `112` -> `112...12`
* `113` -> `113...13`
* `114` -> `114...14`
* `115` -> `115...15`
* ...
Disagg part
Data
synthetic_data <- predDf2 %>%
mutate(spec_only = map(spec, ~pull(., np)),
prev_spec = lag(spec_only),
prev_Flux = lag(Flux),
DF = prev_Flux - Flux,
DFP = 1-DF/prev_Flux
# I could have calulated DFP = Flux / prev_Flux, which is equivalent to this way.
)
Variabiles
# mass of a 1mm particle
m1mm = 3.3 * 10^-6; #%g % Alldgedge 1998 % mass of 1mm particle
w1mm = 2; #% m/day # Alldredge and Gotschalk, methinks % sinking speed of 1mm particle
micron = 1e-6;
# fractle dimension
#C_f = 4 # Usual Way
#ag = 0.26
# So we can conform to usual expectations of alpha and gamma being positive
C_f = C_f # defined earlier
ag = ag # defined earlier
Cm = m1mm
Cw = w1mm
m_vec = Cm * lb_vec ^ alpha;
w_vec = Cw * lb_vec ^ gamma;
test_abun_in <- synthetic_data %>% filter(depth == 25) %>% pull(spec) %>%.[[1]] %>% pull(np)
f_vec0 <- test_abun_in * m_vec * w_vec
F0 <- sum(f_vec0)
test_abun_out <- synthetic_data %>% filter(depth == 50) %>% pull(spec) %>% .[[1]] %>% pull(np)
f_vec1 <- test_abun_out * m_vec * w_vec
F1 <- sum(f_vec1)
DFP <- F1/F0
DFP
[1] 0.4871608
little_lb <- lb_vec[1] - (lb_vec[2] - lb_vec[1])/2 # size of the particle that the UVP can't see anymore. Eg, things actually shrink to this size but then they vanish from the UVP's view. Just leting it be like, the difference in size of the smallest two bins smaller than the smallest bin.
remin_shuffle <- function(abun_in, DFpct, DeltaZ = 10, size = lb_vec, Cm = m1mm, Cw = w1mm, lbv = lb_vec, mv = m_vec, wv = w_vec,
alpha = 0.52, gamma = 0.26, llb = little_lb){
rn = abun_in * lb_vec
ran = abun_in * lb_vec ^ alpha
srn = sum(rn)
sran = sum(ran)
omega = lb_vec[1] ^ (2 * alpha) * abun_in[1]/(lb_vec[1] ^ alpha - llb ^ alpha)
#omega = lb_vec[1] ^ (2 * alpha) * abun_in[1]/(llb ^ alpha - lb_vec[1] ^ alpha) # possible correction
nmw = abun_in * mv * wv
F1 = sum(nmw)
DeltaF = (F1 * DFpct) - F1 # should be negative
#Cr = DeltaF/ (Cm*(1+gamma/alpha) * DeltaZ * (sran + omega));
Cr = DeltaF/ (Cm* DeltaZ * ((1+gamma/alpha) * sran + omega)); # Possible correction
#Cr = DeltaF/ (Cm*(1+gamma/alpha) * DeltaZ * (sran)); # Test
CrCw = Cr/Cw
phi = CrCw * (1+gamma/alpha) * ran *
DeltaZ/
(c(llb, lbv)[1:length(lbv)]^alpha - lbv ^ alpha) # added extra parentheses
Delta_nj_out = phi/lbv ^ gamma
Delta_nj_in = c(phi[2:length(phi)],0)/c(lbv[2:length(phi)],1) ^ gamma
#Delta_nj_in = -c(phi[2:length(phi)],0)/lbv ^ gamma
Delta_nj_net = Delta_nj_in - Delta_nj_out # positive because out is negative
return(list(Cr = Cr, phi = phi, dnet = Delta_nj_net, din = Delta_nj_in, dout = Delta_nj_out))
}
Smooth Shuffle
remin_shuffle_spec <- function(abun_in, ...){
core <- remin_shuffle(abun_in, ...)
abun_in + core$dnet
}
dds_test <- 0.99
trs_spec <- remin_shuffle_spec(test_abun_in, dds_test, 2, llb = .08) # .076, .102, 0.08
trs_spec
[1] 4.977118e+01 3.220295e+01 2.080654e+01 1.324086e+01 8.429664e+00 5.259372e+00 3.412537e+00 2.158349e+00 1.376862e+00 8.570189e-01
[11] 5.706750e-01 3.591209e-01 2.222774e-01 1.423506e-01 9.117745e-02 5.843491e-02 3.667519e-02 2.348329e-02 1.496326e-02 9.434999e-03
[21] 6.126706e-03 3.875805e-03 2.409464e-03 1.576325e-03 8.755244e-04
(pre <- sum(C_f * test_abun_in ^ ag))
[1] 763.788
(expected <- sum(C_f * test_abun_in ^ ag) * dds_test)
[1] 756.1501
(modeled <- sum(C_f * trs_spec ^ ag))
[1] 755.6298
(expected - pre)/pre
[1] -0.01
(modeled - pre)/pre
[1] -0.01068125
(modeled - expected)/expected
[1] -0.0006881335
llb seems to be pretty relevant for getting flux right – presumably I am not handling it correctly. I’d like things to adjust so that it is considered correctly
test_abun_in * dds_test
[1] 4.995311e+01 3.238547e+01 2.091009e+01 1.329027e+01 8.457959e+00 5.266899e+00 3.419150e+00 2.159717e+00 1.377626e+00 8.557674e-01
[11] 5.705486e-01 3.586372e-01 2.217339e-01 1.419959e-01 9.090876e-02 5.825105e-02 3.653090e-02 2.338785e-02 1.489793e-02 9.387706e-03
[21] 6.096721e-03 3.855430e-03 2.395040e-03 1.568487e-03 8.751845e-04
sum((C_f * test_abun_in ^ ag)) * dds_test
[1] 756.1501
explore_llb <- tibble(llb = c(0, 0.01, 0.07, 0.08, 0.09, .1, .101, .1019, .10199)) %>%
mutate(spec = map(llb, ~remin_shuffle_spec(test_abun_in, dds_test, 2, llb = .)))
explore_llb_unwrap <- explore_llb %>%
mutate(spec = map(spec, ~data_frame(lb_vec = lb_vec, spec = .))) %>%
unnest(spec) %>%
pivot_wider(names_from = lb_vec, values_from = spec)
`data_frame()` is deprecated as of tibble 1.1.0.
Please use `tibble()` instead.
[90mThis warning is displayed once every 8 hours.[39m
[90mCall `lifecycle::last_warnings()` to see where this warning was generated.[39m
explore_llb_flux <- explore_llb %>%
mutate(flux = map_dbl(spec, ~sum(C_f * . ^ ag))) %>%
select(-spec)
explore_llb_2 <- left_join(explore_llb_flux, explore_llb_unwrap)
Joining, by = "llb"
#
explore_llb_2
As we approach llb = lb_vec[1]
dds_test <- 0.99
trs_spec <- remin_shuffle_spec(test_abun_in, dds_test, 2, llb = .08) # .076, .102
trs_spec
[1] 4.977118e+01 3.220295e+01 2.080654e+01 1.324086e+01 8.429664e+00 5.259372e+00 3.412537e+00 2.158349e+00 1.376862e+00 8.570189e-01
[11] 5.706750e-01 3.591209e-01 2.222774e-01 1.423506e-01 9.117745e-02 5.843491e-02 3.667519e-02 2.348329e-02 1.496326e-02 9.434999e-03
[21] 6.126706e-03 3.875805e-03 2.409464e-03 1.576325e-03 8.755244e-04
sum(C_f * test_abun_in ^ ag)
[1] 763.788
sum(C_f * trs_spec ^ ag)
[1] 755.6298
remin_smooth_shuffle <- function(abun_in, DFpct, Ipct = 0.9999, ...){
# DFpct: Fractional mass retained between depths
# Ipct: Fractional mass retained between iterations
# ...: Passed to remin_shuffle
IMirror <- 2 - Ipct
abun_est = abun_in
Fpct = DFpct # gets overwritten if we are iterating
# If we are loossing flux, and we loose loose more flux than Ipct
# Iterate remin shuffle only keeping ipct each time
if(DFpct < Ipct){
iters = floor(log(DFpct)/log(Ipct)) # why is this a ratio of log transformed values?
# I need to understand this before I can address the case where DFpct > IMirror
iterFlux = Ipct^iters
Fpct = 1-(iterFlux-DFpct)
for (i in 1:iters){
abun_est = remin_shuffle_spec(abun_in = abun_est, Ipct, ...)
}
}
# If we are gaining flux, and we gain more than IMirror, iterate
if(DFpct > IMirror){
iters = floor(log(DFpct)/log(IMirror))
iterFlux = IMirror^iters
Fpct = 1 - (iterFlux - DFpct)
for (i in 1:iters){
abun_est = remin_shuffle_spec(abun_in = abun_est, IMirror, ...)
}
}
# Deal with remainder. In the case where the loss is less than ipct, or greater than 2-ipct (Imirror), just do this part
abun_est = remin_shuffle_spec(abun_in = abun_est, Fpct, ...)
abun_est
}
DFP = 0.9
trss0 <- remin_shuffle_spec(DFpct = DFP , abun_in = test_abun_in) # .076, .102
trss0
[1] 3.573079e+01 2.919141e+01 1.894655e+01 1.215562e+01 7.757633e+00 4.900530e+00 3.169375e+00 2.021353e+00 1.290119e+00 8.133358e-01
[11] 5.373678e-01 3.405726e-01 2.122546e-01 1.359706e-01 8.733901e-02 5.604447e-02 3.534732e-02 2.265133e-02 1.446004e-02 9.154131e-03
[21] 5.939993e-03 3.766087e-03 2.351742e-03 1.529026e-03 8.252953e-04
trss <- remin_smooth_shuffle(DFpct = DFP, abun_in = test_abun_in) # .076, .102
trss
[1] 3.713991e+01 2.901460e+01 1.883700e+01 1.208027e+01 7.715583e+00 4.869549e+00 3.151720e+00 2.009593e+00 1.283620e+00 8.086840e-01
[11] 5.345717e-01 3.389169e-01 2.112405e-01 1.353414e-01 8.694556e-02 5.580600e-02 3.520412e-02 2.256208e-02 1.440645e-02 9.121834e-03
[21] 5.919266e-03 3.753961e-03 2.344373e-03 1.522550e-03 8.206877e-04
# Confirm whether flux is treated appropriately
sum(C_f * test_abun_in ^ ag) * DFP
[1] 687.4092
sum(C_f * trss0 ^ ag)
[1] 670.6552
sum(C_f * trss ^ ag)
[1] 673.6107
tibble(test_abun_in, trss0, trss, lb_vec) %>% ggplot(aes(x = lb_vec)) + geom_point(aes(y = trss0)) + geom_point(aes(y = trss), shape = 2) + scale_y_log10() + scale_x_log10() + geom_point(aes(y = test_abun_in), shape = 1)

Graveyard for now
# I want to minimize this function here
shuffle_check <- function(abun_in, DFpct, C_f2 = C_f, ag2 = ag, ...){
abun_out <- remin_smooth_shuffle(abun_in, DFpct, ...)
flux_in <- sum(C_f2 * abun_in ^ ag2)
flux_out <- sum(C_f2 * abun_out ^ ag2)
DFPct_actual = flux_out/flux_in
rmse <- (DFpct - DFPct_actual)^2
rmse
}
find_DFP <- function(abun_in, ...){
abun_in_loc = abun_in
sc_wrap <- function(x, abun_in_loc){
shuffle_check(abun_in = abun_in_loc, DFpct = x)
}
adjDfp <- optimize(sc_wrap, c(0, 2), abun_in_loc = test_abun_in)
adjDfp
}
# remin_smooth_shuffle_fix <- function(abun_in, DFpct, ...){
#
# }
DFP = 0.8
shuffle_check(DFpct = DFP , abun_in = test_abun_in)
[1] 0.001137407
find_DFP(abun_in = test_abun_in)
$minimum
[1] 0.9999975
$objective
[1] 2.269221e-13
Data
First, lets calculate Dfpct for each pair of depths, and lets also stagger spec
dummy_function <- function(DFpct, spec, ...){
DFpct * spec
}
dummy_function(0.8, test_abun_in)
[1] 4.036615e+01 2.617008e+01 1.689704e+01 1.073961e+01 6.834714e+00 4.256080e+00 2.762950e+00 1.745226e+00 1.113233e+00 6.915292e-01
[11] 4.610493e-01 2.898079e-01 1.791789e-01 1.147442e-01 7.346163e-02 4.707156e-02 2.951992e-02 1.889927e-02 1.203873e-02 7.586025e-03
[21] 4.926643e-03 3.115499e-03 1.935386e-03 1.267464e-03 7.072198e-04
synthetic_remineralized <- synthetic_data %>% .[-1,] %>% mutate(pred_spec = map2(prev_spec, DFP, remin_smooth_shuffle))
synthetic_remineralized
Did it work?
tibble(
actual = synthetic_remineralized[["spec_only"]][[4]],
predicted = synthetic_remineralized[["pred_spec"]][[4]],
previous = synthetic_remineralized[["prev_spec"]][[4]],
lb = lb_vec
) %>% gather(key = "var", value = "np", actual, predicted, previous) %>%
ggplot(aes(x = lb, y = np, shape = var, col = var), ) + geom_point(size = 3, alpha = 0.75) + scale_x_log10() + scale_y_log10() + labs(x = "# Particles", y = "Size (mm)", col = "Model", shape = "Model")

Frigging finally. Consistant everything I think.
Looks promising! Lets be more systematic about this next! synthetic_remineralized_con Ok. If I calculate flux assuming alpha = 2.3 and gamma = 1.3, I get this stronger than traditional arch effect. I guess because flux attenuates faster than it should.
If I say that alpha + gamma = 0.63 as best fit flux, then I loose more big particles than small ones. More resetting instnaces and now things look really strange.
As of april 21 2020, I’m using alldredge alpha and gamma
But also my different flux profiles are no longer adding up.
As above, but this time, with adjusted DFP to account for “leakage”
Leaks
I’m having a leakage problem. I get nearly, but not quite the right amount of flux out. What if I address this by running remin smooth shuffle and then I optimize the DFPct that it gets so that it returns the right flux output.
I ought to actually figure out what is wrong, but this should give a close answer for most analyses.
Optimization function: What I want it to do. Takes a structure and a DFP. Varies the DFP sent to remin smooth shuffle so the acutal DFP loss is what we want
We need a function where we can pass the DFPct that we are using and the actual DFPct
shuffle_tune <- function(DFpct_toRemin, abun_in, DFpct_target,...){
abun_out <- remin_smooth_shuffle(abun_in, DFpct_toRemin, ...)
flux_in <- sum(C_f_global * abun_in ^ ag_global)
flux_out <- sum(C_f_global * abun_out ^ ag_global)
DFPct_actually_happened <- flux_out/flux_in
rmse <- (DFPct_actually_happened - DFpct_target)^2
rmse
}
stOpt <- optimize(shuffle_tune, c(0, 2), abun_in = test_abun_in, DFpct_target = 0.9)
stOpt
$minimum
[1] 0.9154147
$objective
[1] 3.926557e-11
So for a DFpct of 0.9, I should actually feed it 0.915
Apply stopt over the data set.
optFun <- function(abun_in, DFpct){
opt <- optimize(shuffle_tune, c(0, 2), abun_in = abun_in, DFpct_target = DFpct)
opt$minimum
}
Test optimization
plot(
seq(from = 0.5, to = 1, by = 0.05),
map_dbl(seq(from = 0.5, to = 1, by = 0.05), ~optFun(testSpectrum, .))
)

Apply optimization
synthetic_data_fixDfp <- synthetic_data %>% filter(depth > 0) %>% mutate(use_this_DFpct = map2_dbl(prev_spec, DFP, optFun))
synthetic_data_fixDfp %>% ggplot(aes(x = DFP, y = use_this_DFpct)) + geom_point() + geom_abline(v = 1,h = 1)
Ignoring unknown parameters: v, h

synthetic_data_fixDfp %>% ggplot(aes(x = DFP, y = use_this_DFpct - DFP)) + geom_point() + geom_abline(v = 1,h = 1)
Ignoring unknown parameters: v, h

Redo remineralization
synthetic_remineralized <- synthetic_data_fixDfp %>% .[-1,] %>% mutate(pred_spec = map2(prev_spec, use_this_DFpct, remin_smooth_shuffle))
synthetic_remineralized
Analyzing remineralized data
Indicating which regions are actually loosing flux the whole time. Lets first look at particle numbers, observed minus expected
dfpPlt <- synthetic_remineralized %>% ggplot(aes(y = depth, x = DFP, col = DFP < 1)) + geom_point() + scale_y_reverse()
fluxPlt <- synthetic_remineralized %>% ggplot(aes(y = depth, x = Flux, col = DFP < 1)) + geom_point() + scale_y_reverse()
plot_grid(dfpPlt, fluxPlt)

synthetic_remineralized_proc <- synthetic_remineralized %>% mutate(TP = map_dbl(spec, sum), pred_TP = map_dbl(pred_spec, sum), dif_TP = TP-pred_TP)
dtpP <- synthetic_remineralized_proc %>% ggplot(aes(y = depth, x = dif_TP, col = DFP < 1)) + geom_point() + scale_y_reverse()
ptpP <- synthetic_remineralized_proc %>% ggplot(aes(y = depth, x = pred_TP, col = DFP < 1)) + geom_point() + scale_y_reverse() + scale_x_log10()
tpP <- synthetic_remineralized_proc %>% ggplot(aes(y = depth, x = TP, col = DFP < 1)) + geom_point() + scale_y_reverse() + scale_x_log10()
plot_grid(tpP, ptpP, dtpP)

Now that I’ve fixed this for positive values, we predict absurd numbers of particles much of the time. Lets just look at decreasing parts.
synthetic_remineralized_proc_decreasing <- synthetic_remineralized_proc %>% filter(DFP <= 1)
dtpP <- synthetic_remineralized_proc_decreasing %>% ggplot(aes(y = depth, x = dif_TP, col = DFP < 1)) + geom_point() + scale_y_reverse()
ptpP <- synthetic_remineralized_proc_decreasing %>% ggplot(aes(y = depth, x = pred_TP, col = DFP < 1)) + geom_point() + scale_y_reverse() + scale_x_log10()
tpP <- synthetic_remineralized_proc_decreasing %>% ggplot(aes(y = depth, x = TP, col = DFP < 1)) + geom_point() + scale_y_reverse() + scale_x_log10()
fractpP <- synthetic_remineralized_proc_decreasing %>% ggplot(aes(y = depth, x = dif_TP/TP, col = DFP < 1)) + geom_point() + scale_y_reverse()
plot_grid(tpP, ptpP, dtpP, fractpP)

Oh dear. The model predicts profound particle number attenuation. Actually there isn’t so everything actually just follows flux.
Maybe there’s some better metric. Like fraction of flux on particles smaller than 53 micron or something.
This may have been a dead end. I need to reflect again.
useRow <- which(synthetic_remineralized$DFP > 1)[1]
tibble(
actual = synthetic_remineralized[["spec_only"]][[useRow]],
predicted = synthetic_remineralized[["pred_spec"]][[useRow]],
previous = synthetic_remineralized[["prev_spec"]][[useRow]],
lb = lb_vec
) %>% gather(key = "var", value = "np", actual, predicted, previous) %>%
ggplot(aes(x = lb, y = np, shape = var, col = var), ) + geom_point(size = 3, alpha = 0.75) + scale_x_log10() + scale_y_log10()

Ah. Somethings off here.
test_abun_in
[1] 5.045769e+01 3.271260e+01 2.112130e+01 1.342451e+01 8.543393e+00 5.320100e+00 3.453687e+00 2.181533e+00 1.391541e+00 8.644115e-01
[11] 5.763117e-01 3.622598e-01 2.239736e-01 1.434302e-01 9.182704e-02 5.883945e-02 3.689990e-02 2.362409e-02 1.504842e-02 9.482531e-03
[21] 6.158304e-03 3.894373e-03 2.419233e-03 1.584330e-03 8.840247e-04
trss00 <- remin_smooth_shuffle(DFpct = 1, abun_in = test_abun_in) # .076, .102
trss00
[1] 5.045769e+01 3.271260e+01 2.112130e+01 1.342451e+01 8.543393e+00 5.320100e+00 3.453687e+00 2.181533e+00 1.391541e+00 8.644115e-01
[11] 5.763117e-01 3.622598e-01 2.239736e-01 1.434302e-01 9.182704e-02 5.883945e-02 3.689990e-02 2.362409e-02 1.504842e-02 9.482531e-03
[21] 6.158304e-03 3.894373e-03 2.419233e-03 1.584330e-03 8.840247e-04
trss00 <- remin_shuffle_spec(DFpct = 1.01, abun_in = test_abun_in) # .076, .102
trss00
[1] 5.193038e+01 3.306471e+01 2.133878e+01 1.355140e+01 8.621969e+00 5.362057e+00 3.482118e+00 2.197551e+00 1.401683e+00 8.695191e-01
[11] 5.802061e-01 3.644286e-01 2.251455e-01 1.441762e-01 9.227584e-02 5.911895e-02 3.705516e-02 2.372136e-02 1.510726e-02 9.515371e-03
[21] 6.180135e-03 3.907202e-03 2.425982e-03 1.589860e-03 8.898977e-04
13 April 2020
Hmm. Because of the power law nature of particles and size essentially “all” of the particles are created at each depth. Essentially particle number isn’t a great proxie for disaggregation.
I have a couple of ideas.
One is to look at <53 miron vs >53 micron particles. One could ask: How much biomass or flux moves from one side of that line to the other?
I’d also like to get at things like how particle production relates to flux attenuation. Like how much of flux attenuation is different with the small particles than if there weren’t these small particles.
I could imagine calculating C_r everywhere and then just running a purely prism model propigating downward with that Cr. Seeing what flux is at some key threshold, and then comparing that to actual flux. I’d expect that essentially all of the attenuation woudl be through the production and removal of small particles.
In the back of my mind though, my assumptions about small particles are bugging me. What if they are more recalcitrant than big particles. Then all of the models sort of don’t work, right?
20 April 2020
Some unnesting
synthetic_remineralized_concise <- synthetic_remineralized %>%
mutate(spec2 = map2(spec, prev_spec, ~tibble(.x, prev_np = .y))) %>%
mutate(spec2 = map2(spec2, pred_spec, ~tibble(.x, pred_np = .y))) %>%
select(-c(spec, prev_spec, pred_spec, spec_only))
synthetic_remineralized_unnested <- synthetic_remineralized_concise %>% unnest(spec2)
I wonder what is up with these name reassignments?
Goal: Estimate attenuation of large and small particles. Compare that to total attenuation. (Hopefully they sum to the same thing). Do this for both the model, and for the observed data.
Calculate flux transfer to small particles from disaggregation which should be
ObservedSmallFlux - PredictedSmallFlux Which should equal: PredictedBigFlux - ObservedFlux
sr02 <- synthetic_remineralized_unnested %>%
mutate(prev_flux = C_f * prev_np ^ ag,
pred_flux = C_f * pred_np ^ ag)
sr02Tot <- sr02 %>%
group_by(depth) %>%
summarize(DF = first(DF), DFP = first(DFP),
Flux = sum(flux), Prev_Flux = sum(prev_flux), Pred_Flux = sum(pred_flux))
sr02Small <- sr02 %>%
filter(lb <= 0.53) %>%
group_by(depth) %>%
summarize(Flux = sum(flux), Prev_Flux = sum(prev_flux), Pred_Flux = sum(pred_flux))
sr02Big <- sr02 %>%
filter(lb > 0.53) %>%
group_by(depth) %>%
summarize(Flux = sum(flux), Prev_Flux = sum(prev_flux), Pred_Flux = sum(pred_flux))
sr02All <- sr02Tot %>%
left_join(sr02Small, by = "depth", suffix = c("", "_Small")) %>%
left_join(sr02Big, by = "depth", suffix = c("", "_Big")) %>%
mutate(osps = Flux_Small - Pred_Flux_Small, obpb = Pred_Flux_Big - Flux_Big) # And of course they are not equal
head(sr02All)
Ok. Osps ~= obpb, which means that big flux lost about equals small flux gained due to disaggregation (or whatever proces makes things deviate from the model)
sr02All %>% filter(DFP <= 1) %>% ggplot(aes(y = depth, x = osps, col = DFP)) + geom_point() + scale_y_reverse()

Huh. Look at that. Sometimes osps goes negative, which is to say there is apparent aggregation there. I think.
sr02All %>% filter(DFP <= 1) %>% ggplot(aes(y = DFP, x = osps)) + geom_point() + scale_y_reverse()

Ok. What do I actually want to know here. I think osps devided by the flux attenuation of the big particles (predicted or observed). I want to know what fraction of big particles become small particles vs just dissolve.
Or maybe something else too?
sr02All %>% filter(DFP <= 1) %>% ggplot(aes(y = DF, x = osps)) + geom_point() + scale_y_reverse()

ospsPlt <- sr02All %>% filter(DFP <= 1) %>% ggplot(aes(y = depth, x = osps/DF)) + geom_point() + scale_y_reverse() + labs(x = "Disaggregation") + theme_cowplot() + geom_vline(xintercept = 0, col = "gray50")
ospsPlt

Huh.Look at this. We have disaggregation happening in the surface, at ~500m (after an uptick in flux, which gets erased, so maybe a zooplankton thing) and again at the bottom of the OMZ. We have net aggregation in the OMZ core, for the most part.
This is cool!
plot_grid(fluxPlt + labs(col = "IsFluxDecreasing?") + theme_cowplot() + theme(legend.position = "none"),
ospsPlt + theme_cowplot())

I think the stuff below is misguided and the thing above, especially combined wiht illustrations of where flux increases, will be sufficiently informative.
Disaggregation are positive values here, btw. Aggregation are negative values.
sr02All %>% filter(DFP <= 1) %>% ggplot(aes(y = depth, x = osps/(Prev_Flux_Big- Flux_Big))) + geom_point() + scale_y_reverse() + scale_x_log10()

Why is the above ever more than one? More stuff disaggregates than disapears? If it disapears, doesn’t it disaggregate?
plot_prev_big <- sr02All %>% filter(DFP <= 1) %>% ggplot(aes(y = depth, x = Prev_Flux_Big-Flux_Big)) + geom_point() + scale_y_reverse()
plot_big <- sr02All %>% filter(DFP <= 1) %>% ggplot(aes(y = depth, x = Prev_Flux_Big - Pred_Flux_Big)) + geom_point() + scale_y_reverse()
plot_pred_big <- sr02All %>% filter(DFP <= 1) %>% ggplot(aes(y = depth, x = Pred_Flux_Big)) + geom_point() + scale_y_reverse() + scale_x_log10()
cowplot::plot_grid(plot_prev_big, plot_big, plot_pred_big)

Huh. Sometimes the big flux goes up, even when total flux goes down, both in observed and predicted space. I guess this makes sense if we have attenuation, but a flattening of the curve.
sr02All %>% filter(DFP <= 1) %>% ggplot(aes(y = depth, x = osps/(Prev_Flux_Big- Pred_Flux_Big))) + geom_point() + scale_y_reverse() + scale_x_log10()

All this and more tomorrow.
plot_grid(dfpPlt, fluxPlt)

p <- sr02All %>% filter(DFP <= 1) %>% ggplot(aes(y = depth, x = osps/(Prev_Flux_Big - Flux_Big))) + geom_point() + scale_y_reverse()
plotly::ggplotly(p)
Registered S3 method overwritten by 'data.table':
method from
print.data.table
Registered S3 method overwritten by 'htmlwidgets':
method from
print.htmlwidget tools:rstudio
875 m is the most clea example. Lets look at the profiles and see what is happening there.
tibble( actual = synthetic_remineralized[[“spec_only”]][[4]], predicted = synthetic_remineralized[[“pred_spec”]][[4]], previous = synthetic_remineralized[[“prev_spec”]][[4]], lb = lb_vec ) %>% gather(key = “var”, value = “np”, actual, predicted, previous) %>% ggplot(aes(x = lb, y = np, shape = var, col = var), ) + geom_point(size = 3, alpha = 0.75) + scale_x_log10() + scale_y_log10()
synthetic_remineralized_unnested %>% filter(depth == 875, lb < 0.2) %>% select(lb, actual = np, previous = prev_np, predicted = pred_np) %>%
gather(key = "var", value = "np", actual, previous, predicted) %>%
ggplot(aes(x = lb, y = np, shape = var, col = var), ) + geom_point(size = 3, alpha = 0.75) + scale_x_log10() + scale_y_log10()

synthetic_remineralized_unnested %>% filter(depth == 875, lb > 10) %>% select(lb, actual = np, previous = prev_np, predicted = pred_np) %>%
gather(key = "var", value = "np", actual, previous, predicted) %>%
ggplot(aes(x = lb, y = np, shape = var, col = var), ) + geom_point(size = 3, alpha = 0.75) + scale_x_log10() + scale_y_log10()

synthetic_remineralized_unnested %>% filter(depth == 875) %>% mutate(previous = prev_np - np, predicted = pred_np - np) %>% select(lb, previous, predicted) %>%
gather(key = "var", value = "np",previous, predicted) %>%
ggplot(aes(x = lb, y = 1/(np), shape = var, col = var), ) + geom_point(size = 3, alpha = 0.75) + scale_x_log10()

Huh. The curve appears to be flattening more than we would expect from random chance. But there are still more particles because of shenanigans in the smallist bin.
So these positive numbers might in fact be places with negative osps (but also flattening and so an opposite of usual change in large particles)
I wonder how even to address this.
LS0tCnRpdGxlOiAiRGlhZ25vc2VkIERpc3NhZ2dyZWdhdGlvbiBUcnkgMiIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKU2VxdWFsIHRvIFRyeVRvRGlhZ25vc2VEaXNhZ2cuUm1kCldoaWNoIGRpZG4ndCB3b3JrIHRoZSBmaXJzdCB0aW1lIGFuZCBraW5kIG9mIGJlY2FtZSBhIG1lc3MuCkFuZCBGbHV4SW50ZXJjZXB0U2lsbHluZXNzLlJtZAoKMjQgTWFyY2ggMjAxOQoKU3RhcnQgd2l0aCBjb3JlIHBhcnRzIG9mIHRoZSBGbHV4SW50ZXJjZXB0U2lsbHluZXNzIHNvbHV0aW9uCgpUaGVuIGxldHMgcnVuIGRpc3NhZ2dyZWdhdGlvbiBvbiB0aGUgZmx1eCBwcm9maWxlLgoKIyBNYWtpbmcgYSBzeW50aGV0aWMgZGF0YSBzZXQuCgpEYXRhIHRvIG1ha2UgYmlubmVkX0VhY2hTaXplX2FsZC5jc3YgY29tZSBmcm9tIE5vcm1hbGl6ZV9VVlBfRmx1eC5SbWQKV2hpY2ggaW4gdHVybiBwdWxscyBkYXRhIGdlbmVyYXRlZCBieSBVVlAyMDE3LlJtZAoKYGBge3IsIG1lc3NhZ2U9IEZBTFNFfQpvcHRpb25zKHJlYWRyLmRlZmF1bHRfbG9jYWxlPXJlYWRyOjpsb2NhbGUodHo9Ik1leGljby9HZW5lcmFsIikpCnNvdXJjZSgiVVZQXzIwMTdfbGlicmFyeS5SIikKI2RhdGFQMiA8LSBicmluZ19pbl9wMigpCiN0d2luMDEgPC0gbWFrZV90d2luX2RmX2xpc3QoZGF0YVAyKQojRVMwMSA8LSB0d2luMDFbWzFdXSAlPiUgZmlsdGVyKGRlcHRoIDw9IDEwMDAsIHByb2ZpbGUgPT0gInN0bl8wNDMiKQojRFMwMSA8LSB0d2luMDFbWzJdXSAlPiUgZmlsdGVyKGRlcHRoIDw9IDEwMDAsIHByb2ZpbGUgPT0gInN0bl8wNDMiKQojICMgTW9kaWZpY2F0aW9uLCBtZXJnZSBhbGwgc3RhdGlvbnMgYW5kIHNlZSB3aGF0IGhhcHBlbnMuCiMgRVMwMSA8LSB0d2luMDFbWzFdXSAjJT4lIGZpbHRlcihkZXB0aCA8PSAxMDAwKQojIERTMDEgPC0gdHdpbjAxW1syXV0gIyU+JSBmaWx0ZXIoZGVwdGggPD0gMTAwMCkKCkVTMDEgPC0gcmVhZF9jc3YoImRhdGFPdXQvYmlubmVkX0VhY2hTaXplLmNzdiIpICU+JSBmaWx0ZXIoZGVwdGggPD0gMTAwMCkKRFMwMSA8LSByZWFkX2NzdigiZGF0YU91dC9iaW5uZWRfRGVwdGhTdW1tYXJ5LmNzdiIpICU+JSBmaWx0ZXIoZGVwdGggPD0gMTAwMCkKdHdpblMgPC0gbGlzdChFUzAxLCBEUzAxKQpgYGAKCmBgYHtyfQpTaW1wbGVCaW5zIDwtIHNlcShmcm9tID0gMCwgdG8gPSAxMTAwLCBieSA9IDEwMCkKYGBgIAoKYGBge3J9CiNkZWJ1ZyhiaW5fZGVwdGhzKQpiaW5uZWRTIDwtIGJpbl9kZXB0aHModHdpblMsIGJpbnMgPSBTaW1wbGVCaW5zKSAlPiUgY2FsY19wc2QKYGBgCgpgYGB7cn0KRVNTIDwtIGJpbm5lZFNbWzFdXQpEU1MgPC0gYmlubmVkU1tbMl1dCmBgYAoKCgojIERpZmZlcmVudCBkaXJlY3Rpb24KVW5pdHMgb2YgbnBhcnRpY2xlcwoKYGBge3J9CiMgbXlTcGVjIDwtIGMoMy4zMjQwNTY3ODUwOTMxNiwgMS44NTg3Nzg1NjU2MTU1NCwgMS4wMzM1OTMwNTkxNzg3NywgMC41NjQ4ODA4ODUyNDAzNDksIAojIDAuMzA5MDc5NTIzNjcwOTU5LCAwLjE2NTQyNDA2NjcwNjI0NiwgMC4wOTI1MjcwMjMyNDgwNzgzLCAwLjA1MDI0OTE1NzkxODMzMjcsIAojIDAuMDI3NTc2NTgwODU4MTk3NywgMC4wMTQ3MzI3OTYyMzIyMDQzLCAwLjAwODQ3MzQyMjI4MTQ4MTE2LCAKIyAwLjAwNDU3MDc5NTI2OTE4NDAzLCAwLjAwMjQyNjU3NTE0NDgxODY5LCAwLjAwMTMzODQwMDY0NTg4OTMxLCAKIyAwLjAwMDczNzY4MjMxMjYyMDI3LCAwLjAwMDQwNjY4ODMxOTIzOTQyNywgMC4wMDAyMTkyMjg5MjIyNjE2NCwgCiMgMC4wMDAxMjA4MzE3NTQ1NzUwNzMsIDYuNjIyMzM1NzE4NTE3MWUtMDUsIDMuNTg5NzkxOTI0ODM5MTdlLTA1LCAKIyAyLjAwNzk5NzEzNzI5Nzk0ZS0wNSwgMS4wOTExNTE2Njk2OTM1NWUtMDUsIDUuODI2NDU5MTQxNzI3OTFlLTA2LCAKIyAzLjI4OTA4Mjc1NDYwMjEyZS0wNiwgMS41NzcwMjcxNTEzN2UtMDYpCgpsYl92ZWMgPC0gYygwLjEwMiwgMC4xMjgsIDAuMTYxLCAwLjIwMywgMC4yNTYsIDAuMzIzLCAwLjQwNiwgMC41MTIsIDAuNjQ1LCAKMC44MTMsIDEuMDIsIDEuMjksIDEuNjMsIDIuMDUsIDIuNTgsIDMuMjUsIDQuMSwgNS4xNiwgNi41LCA4LjE5LCAKMTAuMywgMTMsIDE2LjQsIDIwLjYsIDI2KQoKYmluc2l6ZV92ZWMgPC0gYygwLjAyNiwgMC4wMzMsIDAuMDQyLCAwLjA1MywgMC4wNjcsIDAuMDgzLCAwLjEwNiwgMC4xMzMsIDAuMTY4LCAKMC4yMDcsIDAuMjcsIDAuMzQsIDAuNDIsIDAuNTMsIDAuNjcsIDAuODUsIDEuMDYsIDEuMzQsIDEuNjksIAoyLjExLCAyLjcsIDMuNCwgNC4yLCA1LjQsIDYpCmBgYAoKYGBge3J9CiMgQ19mID0gNAojYWcgPSAwLjI2ICMgYXMgaW4gcmVzdCBvZiB1bml2ZXJzZQpDX2YgPSAxMC41MSAjIGZvciBub3cKYWxwaGEgPSAuNTIKZ2FtbWEgPSAuMjYKYWcgPSBhbHBoYSArIGdhbW1hCgpDX2ZfZ2xvYmFsIDwtIENfZgphbHBoYV9nbG9iYWwgPC0gYWxwaGEKZ2FtbWFfZ2xvYmFsIDwtIGdhbW1hCmFnX2dsb2JhbCA8LSBhZwojIG15U3BlYwojIG15RGYgPC0gdGliYmxlKGxiX3ZlYywgbXlTcGVjLCBiaW5zaXplX3ZlYykKYGBgCgoKYGBge3J9Cm1ha2Vfc3BlY3RydW0gPC0gZnVuY3Rpb24oaWNwLCBwc2QsIGJpbnMgPSBsYl92ZWMsIGJpbnNpemVzID0gYmluc2l6ZV92ZWMpewogIENfbiA9IGV4cChpY3ApCiAgbm5wID0gKENfbiAqIGJpbnMgXiBwc2QpICogKGJpbnNpemVzKQogIG5ucAp9Cgp0ZXN0U3BlY3RydW0gPC0gbWFrZV9zcGVjdHJ1bSggLTIsIC0zLjUpCnRlc3RTcGVjdHJ1bQpgYGAKCiMgUG9zdCBmb3IgU3RhY2sgb3ZlcmZsb3csIGF0IGxlYXN0IGluIHRob3J5CgpgYGB7cn0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoY293cGxvdCkKYGBgCgoKYGBge3J9CnBhcnRpY2xlX2RhdGEgPC0gRVNTICU+JSBzZWxlY3QoLW5wYXJ0aWNsZXMsIC1uX25wYXJ0aWNsZXMsIC10aW1lLCAtYmluc2l6ZSkKYGBgCgoKSSBoYXZlIGEgZGF0YXNldCBpbiB3aGljaCBJIGhhdmUgbWVhc3VyZWQgcGFydGljbGVzIG9mIGRpZmZlcmVudCBzaXplcyBhdCBkaWZmZXJlbnQgZGVwdGhzLiBFYWNoIGRlcHRoIGhhcyBzZXZlcmFsIHNpemUgYmlucyBhc3NvY2lhdGVkIHdpdGggaXQKbGI6IGxvd2VyIGJvdW5kIG9mIHBhcnRpY2xlIHNpemUgKG1tKQp1YjogdXBwZXIgYm91bmQgb2YgcGFydGljbGUgc2l6ZSAobW0pCnZvbDogYW1vdW50IG9mIHdhdGVyIHNhbXBsZWQgcGVyIGRlcHRoIChsKQpUb3RhbFBhcnRpY2xlczogVGhlIG51bWJlciBvZiBwYXJ0aWNsZXMgc2VlbiBpbiB0aGF0IHZvbHVtZSAoIykKZGVwdGg6IFRoZSBkZXB0aCB3ZSBhcmUgc2FtcGxpbmcgKG0pCgpXZSBwcm9jZXNzIHRoZSBkYXRhIHNsaWdodGx5CmBgYHtyfQpwYXJ0aWNsZV9kYXRhX3Byb2Nlc3NlZCA8LSBwYXJ0aWNsZV9kYXRhICU+JSAKICBtdXRhdGUoYmluc2l6ZSA9IHViLWxiLCAjIHNpemUgb2YgcGFydGljbGUgYmlucwogICAgICAgICBucGFydGljbGVzID0gVG90YWxQYXJ0aWNsZXMvdm9sLCAjIHBhcnRpY2xlcyBub3JtYWxpemVkIHRvIHZvbHVtZSAoIy9MKQogICAgICAgICBuX25wYXJ0aWNsZXMgPSBucGFydGljbGVzL2JpbnNpemUsICMgcGFydGljbGVzIG5vcm1hbGl6ZWQgdG8gdm9sdW1lIGFuZCBiaW4gc2l6ZSAoIy9ML21tKQogICkKcGFydGljbGVfZGF0YV9wcm9jZXNzZWQKYGBgCgpJIG1vZGVsIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBwYXJ0aWNsZXMgYW5kIGRlcHRoIGFzIGEgcG93ZXIgbGF3IGZ1bmN0aW9uLiBUaGF0IGlzIHRoZSBsb2cgb2YgdGhlIHBhcnRpY2xlIHNpemUgaXMgbGluZWFybHkgcmVsYXRlZCB0byB0aGUgbG9nIG9mIHRoZSBzaXplIGFuZCB2b2x1bWUgbm9ybWFsaXplZCBwYXJ0aWNsZSBudW1iZXJzLiBUbyBhY2NvdW50IGZvciB6ZXJvcywgSSB1c2UgYSBwb2lzc29uIGdsbQpgYGB7cn0KbXlHbG0gPC0gZnVuY3Rpb24oZGYpewogIGdsbShUb3RhbFBhcnRpY2xlcyB+IGxvZyhsYiksIG9mZnNldCA9IGxvZyh2b2wgKiBiaW5zaXplKSwgZmFtaWx5ID0gcG9pc3NvbiwgZGF0YSA9IGRmKQp9CnBhcnRpY2xlX2ljcF9wc2QgPC0gcGFydGljbGVfZGF0YV9wcm9jZXNzZWQgJT4lIG5lc3QoLWRlcHRoLCAtcHJvZmlsZSkgJT4lIAogIG11dGF0ZShtb2RlbCA9IG1hcChkYXRhLCBteUdsbSkpICU+JQogIG11dGF0ZSh0aWRpZWQgPSBtYXAobW9kZWwsIHRpZHkpKSAlPiUKICB1bm5lc3QodGlkaWVkKSAlPiUKICBzZWxlY3QoZGVwdGgsIHByb2ZpbGUsIHRlcm0sIGVzdGltYXRlKSAlPiUKICBzcHJlYWQoa2V5ID0gdGVybSwgdmFsdWUgPSBlc3RpbWF0ZSkgJT4lCiAgcmVuYW1lKGljcCA9IGAoSW50ZXJjZXB0KWAsIHBzZCA9IGBsb2cobGIpYCkKcGFydGljbGVfaWNwX3BzZApgYGAKCk9uZSBtb3JlIHRoaW5nLiBJIGFsc28gY2FyZSBhYm91dCB0aGUgcGFydGljbGUgZmx1eC4gRmx1eCBpcyB0aGUgc3VtIG9mIGFsbCBvZiB0aGUgcGFydGljbGVzIHRpbWVzIHRoZWlyIHNpbmtpbmcgc3BlZWQsIHRpbWVzIHRoZWlyIG1hc3MuIEkgcmVsYXRlIGZsdXggdG8gbWFzcyBhcyBmb2xsb3dzCkZsdXh7aW4gYSBiaW4gb2YgZGlhbXRlciBEfSA9IENfZiAqIEQgXiBhZwpDZiBhbmQgYW5kIGFnIGFyZSBjb25zdGFudHMgCkNmID0gNCwgYWcgPSAwLjIzCkFuZCBUb3RhbCBGbHV4ID0gc3VtX0R7Rmx1eChEKX0KRmx1eCBpcyBjYWxjdWxhdGVkIGJhc2VkIG9uIHRoZSBwYXJ0aWNsZXMgbm9ybWFsaXplZCB0byB2b2x1bWUKCmBgYHtyfQojQ19mID0gNAojYWcgPSAwLjIzCnBhcnRpY2xlX2ZsdXggPC0gcGFydGljbGVfZGF0YV9wcm9jZXNzZWQgJT4lIAogIG11dGF0ZShmbHV4ID0gKENfZiAqIG5wYXJ0aWNsZXMgXiBhZykpICU+JQogIGdyb3VwX2J5KGRlcHRoLCBwcm9maWxlKSAlPiUKICBzdW1tYXJpemUoRmx1eCA9IHN1bShmbHV4KSkKcGFydGljbGVfZmx1eApgYGAKCgoKSSBjb21iaW5lIHRoZSBmbHV4IGRhdGEgd2l0aCB0aGUgUFNEIGFuZCBpbnRlcmNlcHQgZGF0YQpgYGB7cn0KcGFydGljbGVfaXBmIDwtIGxlZnRfam9pbihwYXJ0aWNsZV9pY3BfcHNkLCBwYXJ0aWNsZV9mbHV4LCBieSA9ICJkZXB0aCIpCnBhcnRpY2xlX2lwZgpgYGAKCmBgYHtyfQpwRmx1eCA8LSBnZ3Bsb3QocGFydGljbGVfaXBmLCBhZXMoeCA9IEZsdXgsIHkgPSBkZXB0aCkpICsgc2NhbGVfeV9yZXZlcnNlKCkgKyBnZW9tX3BvaW50KCkKcFBTRCA8LSBnZ3Bsb3QocGFydGljbGVfaXBmLCBhZXMoeCA9IHBzZCwgeSA9IGRlcHRoKSkgKyBzY2FsZV95X3JldmVyc2UoKSArIGdlb21fcG9pbnQoKQpwaWNwIDwtIGdncGxvdChwYXJ0aWNsZV9pcGYsIGFlcyh4ID0gaWNwLCB5ID0gZGVwdGgpKSArIHNjYWxlX3lfcmV2ZXJzZSgpICsgZ2VvbV9wb2ludCgpCnBsb3RfZ3JpZChwRmx1eCwgcFBTRCwgcGljcCwgbnJvdyA9IDEpCmBgYAoKTm93LCBmb3IgInJlYXNvbnMiLCBJIHdhbnQgdG8gc21vb3RoIHRoZXNlIHByb2ZpbGVzIG91dCBhbmQgaW50ZXJwb2xhdGUgc29tZSBuZXcgc3BlY3RyYS4gRmlyc3QsIEkgdXNlIGdhbXMgdG8gbW9kZWwgdGhlIHRocmVlIHByb2ZpbGVzLgoKYGBge3J9CmdhbVBTRCA8LSBnYW0ocHNkIH4gcyhkZXB0aCksIGRhdGEgPSBwYXJ0aWNsZV9pcGYpCmdhbWljcCA8LSBnYW0oaWNwIH4gcyhkZXB0aCksIGRhdGEgPSBwYXJ0aWNsZV9pcGYpCmdhbUZsdXggPC0gZ2FtKEZsdXggfiBzKGRlcHRoKSwgZmFtaWx5ID0gIkdhbW1hIiwgZGF0YSA9IHBhcnRpY2xlX2lwZikKYGBgCgpgYGB7cn0KcHJlZERhdGEgPC0gdGliYmxlKGRlcHRoID0gc2VxKGZyb20gPSAwLCB0byA9IDEwMDAsIGJ5ID0gMjUpKQpwcmVkUFNEIDwtIHByZWRpY3QoZ2FtUFNELCBwcmVkRGF0YSwgdHlwZSA9ICJyZXNwb25zZSIpCnByZWRpY3AgPC0gcHJlZGljdChnYW1pY3AsIHByZWREYXRhLCB0eXBlID0gInJlc3BvbnNlIikKcHJlZEZsdXggPC0gcHJlZGljdChnYW1GbHV4LCBwcmVkRGF0YSwgdHlwZSA9ICJyZXNwb25zZSIpCgpwcmVkRGYgPC0gdGliYmxlKGRlcHRoID0gcHJlZERhdGEkZGVwdGgsIHBzZCA9IHByZWRQU0QsIGljcCA9IHByZWRpY3AsIEZsdXggPSBwcmVkRmx1eCkKcHJlZERmCmBgYAoKYGBge3J9CnBGbHV4IDwtIGdncGxvdChwcmVkRGYsIGFlcyh4ID0gRmx1eCwgeSA9IGRlcHRoKSkgKyBzY2FsZV95X3JldmVyc2UoKSArIGdlb21fcG9pbnQoKSArIHNjYWxlX3hfY29udGludW91cyhsaW1pdHMgPSBjKDAsIDIwMCkpCnBQU0QgPC0gZ2dwbG90KHByZWREZiwgYWVzKHggPSBwc2QsIHkgPSBkZXB0aCkpICsgc2NhbGVfeV9yZXZlcnNlKCkgKyBnZW9tX3BvaW50KCkKcEludCA8LSBnZ3Bsb3QocHJlZERmLCBhZXMoeCA9IGljcCwgeSA9IGRlcHRoKSkgKyBzY2FsZV95X3JldmVyc2UoKSArIGdlb21fcG9pbnQoKSAKcGxvdF9ncmlkKHBGbHV4LCBwUFNELCBwSW50LCBucm93ID0gMSkKYGBgCgpTbyBuZXh0LCBJJ20gZ29pbmcgdG8gcmVjcmVhdGUgc3BlY3RyYSBmcm9tIHBzZCBhbmQgaW50LiBJIHdpbGwgc2VlIGlmIHRoZXNlIHJlY3JlYXRlZCBzcGVjdHJhIGFwcHJveGltYXRlIHRoZSBvYnNlcnZlZCAiRmx1eCIgdmFsdWVzLgoKUmVjYWxsIHRoYXQgcHNkIGFuZCBpbnQgcmVsYXRlIHRvIGJpbnNpemUgYW5kIHZvbHVtZSBub3JtYWxpemVkIHBhcnRpY2xlIG51bWJlcnMuCgpgYGB7cn0KbGJfdmVjID0gcGFydGljbGVfZGF0YSAlPiUgcHVsbChsYikgJT4lIHVuaXF1ZSAjIHRoZSBwYXJ0aWNsZSBzaXplcwpiaW5zaXplX3ZlYyA9IHBhcnRpY2xlX2RhdGFfcHJvY2Vzc2VkICU+JSBwdWxsKGJpbnNpemUpICU+JSB1bmlxdWUgIyB0aGUgcGFydGljbGUgc2l6ZXMKCm1ha2Vfc3BlY3RydW0gPC0gZnVuY3Rpb24oaWNwLCBwc2QsIGJpbnMgPSBsYl92ZWMsIGJpbnNpemVzID0gYmluc2l6ZV92ZWMpewogIENfbiA9IGV4cChpY3ApCiAgbm5wID0gKENfbiAqIGJpbnMgXiBwc2QpICogKGJpbnNpemVzKQogIG5ucAp9CgptYWtlX3NwZWN0cnVtIDwtIGZ1bmN0aW9uKGljcCwgcHNkLCBiaW5zID0gbGJfdmVjLCBiaW5zaXplcyA9IGJpbnNpemVfdmVjKXsKICBubnAgPSBleHAobG9nKGJpbnMpICogcHNkICsgaWNwKQogIG5wID0gbm5wICogYmluc2l6ZXMKICB0aWJibGUobGIgPSBiaW5zLCBucCkKfQoKCnByZWREZiA8LSBwcmVkRGYgJT4lIG11dGF0ZShzcGVjID0gbWFwMihpY3AsIHBzZCwgbWFrZV9zcGVjdHJ1bSkpCmBgYAoKYGBge3J9CnByZWREZltbInNwZWMiXV1bWzVdXSAlPiUgbXV0YXRlKGZsdXggPSAoQ19mICogbnAgXmFnKSkgJT4lIHN1bW1hcml6ZShGbHV4ID0gc3VtKGZsdXgpKQpgYGAKCgpOb3csIEkgcmUtY2FsY3VsYXRlIGZsdXggZnJvbSBzcGVjLgoKYGBge3J9CnN1bWZsdXggPC0gZnVuY3Rpb24oZGYpewogIGRmICU+JSBwdWxsKGZsdXgpICU+JSBzdW0KfQpwcmVkRGYyIDwtIHByZWREZiAlPiUgCiAgbXV0YXRlKHNwZWMgPSBtYXAoc3BlYywgCiAgICAgICAgICAgICAgICAgICAgLiAlPiUgbXV0YXRlKGZsdXggPSAoQ19mICogbnAgXiBhZykpCiAgICAgICAgICAgICAgICAgICAgKSkgJT4lCiAgI211dGF0ZShGbHV4ID0gbWFwKHNwZWMsIH4gLiAlPiUgc3VtbWFyaXNlKEZsdXggPSBzdW0oZmx1eCkpKSkKICBtdXRhdGUoRmx1eCA9IG1hcF9kYmwoc3BlYywgc3VtZmx1eCkpCmBgYAoKYGBge3J9CnBQcmVkRmx1eCA8LSBnZ3Bsb3QoZGF0YSA9IHByZWREZjIsIGFlcyh5ID0gZGVwdGgsIHggPSBGbHV4KSkgKyBnZW9tX3BvaW50KCkgKyBzY2FsZV95X3JldmVyc2UoKSArIHNjYWxlX3hfY29udGludW91cyhsaW1pdHMgPSBjKDAsIDIwMCkpCnBsb3RfZ3JpZChwRmx1eCwgcFByZWRGbHV4KQpgYGAKCiMgVW5uZXN0IFByZWREZgoKYGBge3J9CnByZWREZjMgPC0gcHJlZERmMiAlPiUgdW5uZXN0KHNwZWMpCmBgYAoKIyBEaXNhZ2cgcGFydAoKIyMgRGF0YQpgYGB7cn0Kc3ludGhldGljX2RhdGEgPC0gcHJlZERmMiAlPiUgCiAgbXV0YXRlKHNwZWNfb25seSA9IG1hcChzcGVjLCB+cHVsbCguLCBucCkpLAogICAgcHJldl9zcGVjID0gbGFnKHNwZWNfb25seSksIAogICAgICAgICBwcmV2X0ZsdXggPSBsYWcoRmx1eCksCiAgICAgICAgIERGID0gcHJldl9GbHV4IC0gRmx1eCwKICAgICAgICAgREZQID0gMS1ERi9wcmV2X0ZsdXgKICAgICMgSSBjb3VsZCBoYXZlIGNhbHVsYXRlZCBERlAgPSBGbHV4IC8gcHJldl9GbHV4LCB3aGljaCBpcyBlcXVpdmFsZW50IHRvIHRoaXMgd2F5LgogICkKYGBgCgojIyBWYXJpYWJpbGVzCmBgYHtyfQoKIyBtYXNzIG9mIGEgMW1tIHBhcnRpY2xlCm0xbW0gPSAzLjMgKiAxMF4tNjsgIyVnICUgQWxsZGdlZGdlIDE5OTggJSBtYXNzIG9mIDFtbSBwYXJ0aWNsZQp3MW1tID0gMjsgIyUgbS9kYXkgIyBBbGxkcmVkZ2UgYW5kIEdvdHNjaGFsaywgbWV0aGlua3MgJSBzaW5raW5nIHNwZWVkIG9mIDFtbSBwYXJ0aWNsZQptaWNyb24gPSAxZS02OwojIGZyYWN0bGUgZGltZW5zaW9uCiNDX2YgPSA0ICMgVXN1YWwgV2F5CiNhZyA9IDAuMjYKCiMgU28gd2UgY2FuIGNvbmZvcm0gdG8gdXN1YWwgZXhwZWN0YXRpb25zIG9mIGFscGhhIGFuZCBnYW1tYSBiZWluZyBwb3NpdGl2ZQpDX2YgPSBDX2YgIyBkZWZpbmVkIGVhcmxpZXIKYWcgPSBhZyAjIGRlZmluZWQgZWFybGllcgoKQ20gPSBtMW1tCkN3ID0gdzFtbQptX3ZlYyA9ICBDbSAqIGxiX3ZlYyBeIGFscGhhOwp3X3ZlYyA9IEN3ICogbGJfdmVjIF4gZ2FtbWE7Cgp0ZXN0X2FidW5faW4gPC0gc3ludGhldGljX2RhdGEgJT4lIGZpbHRlcihkZXB0aCA9PSAyNSkgJT4lIHB1bGwoc3BlYykgJT4lLltbMV1dICU+JSBwdWxsKG5wKQoKZl92ZWMwIDwtIHRlc3RfYWJ1bl9pbiAqIG1fdmVjICogd192ZWMKRjAgPC0gc3VtKGZfdmVjMCkKCnRlc3RfYWJ1bl9vdXQgPC0gc3ludGhldGljX2RhdGEgJT4lIGZpbHRlcihkZXB0aCA9PSA1MCkgJT4lIHB1bGwoc3BlYykgJT4lIC5bWzFdXSAlPiUgcHVsbChucCkKZl92ZWMxIDwtIHRlc3RfYWJ1bl9vdXQgKiBtX3ZlYyAqIHdfdmVjCkYxIDwtIHN1bShmX3ZlYzEpCkRGUCA8LSBGMS9GMApERlAKCmxpdHRsZV9sYiA8LSBsYl92ZWNbMV0gLSAobGJfdmVjWzJdIC0gbGJfdmVjWzFdKS8yICMgc2l6ZSBvZiB0aGUgcGFydGljbGUgdGhhdCB0aGUgVVZQIGNhbid0IHNlZSBhbnltb3JlLiBFZywgdGhpbmdzIGFjdHVhbGx5IHNocmluayB0byB0aGlzIHNpemUgYnV0IHRoZW4gdGhleSB2YW5pc2ggZnJvbSB0aGUgVVZQJ3Mgdmlldy4gSnVzdCBsZXRpbmcgaXQgYmUgbGlrZSwgdGhlIGRpZmZlcmVuY2UgaW4gc2l6ZSBvZiB0aGUgc21hbGxlc3QgdHdvIGJpbnMgc21hbGxlciB0aGFuIHRoZSBzbWFsbGVzdCBiaW4uCmBgYAoKYGBge3J9CnJlbWluX3NodWZmbGUgPC0gZnVuY3Rpb24oYWJ1bl9pbiwgREZwY3QsIERlbHRhWiA9IDEwLCBzaXplID0gbGJfdmVjLCBDbSA9IG0xbW0sIEN3ID0gdzFtbSwgbGJ2ID0gbGJfdmVjLCBtdiA9IG1fdmVjLCB3diA9IHdfdmVjLAogICAgICAgICAgICAgICAgICAgICAgICAgIGFscGhhID0gMC41MiwgZ2FtbWEgPSAwLjI2LCBsbGIgPSBsaXR0bGVfbGIpewogcm4gPSBhYnVuX2luICogbGJfdmVjCiByYW4gPSBhYnVuX2luICogbGJfdmVjIF4gYWxwaGEKIHNybiA9IHN1bShybikKIHNyYW4gPSBzdW0ocmFuKQogCiBvbWVnYSA9IGxiX3ZlY1sxXSBeICgyICogYWxwaGEpICAqIGFidW5faW5bMV0vKGxiX3ZlY1sxXSBeIGFscGhhIC0gbGxiIF4gYWxwaGEpCiAjb21lZ2EgPSBsYl92ZWNbMV0gXiAoMiAqIGFscGhhKSAgKiBhYnVuX2luWzFdLyhsbGIgXiBhbHBoYSAtIGxiX3ZlY1sxXSBeIGFscGhhKSAjIHBvc3NpYmxlIGNvcnJlY3Rpb24KIAogbm13ID0gYWJ1bl9pbiAqIG12ICogd3YKIEYxID0gc3VtKG5tdykKIERlbHRhRiA9IChGMSAqIERGcGN0KSAtIEYxICMgc2hvdWxkIGJlIG5lZ2F0aXZlCiAKICNDciA9IERlbHRhRi8gKENtKigxK2dhbW1hL2FscGhhKSAqIERlbHRhWiAqIChzcmFuICsgb21lZ2EpKTsKIENyID0gRGVsdGFGLyAoQ20qICBEZWx0YVogKiAoKDErZ2FtbWEvYWxwaGEpICogc3JhbiArIG9tZWdhKSk7ICMgUG9zc2libGUgY29ycmVjdGlvbgogI0NyID0gRGVsdGFGLyAoQ20qKDErZ2FtbWEvYWxwaGEpICogRGVsdGFaICogKHNyYW4pKTsgIyBUZXN0CiBDckN3ID0gQ3IvQ3cKIAogcGhpID0gQ3JDdyAqICgxK2dhbW1hL2FscGhhKSAqIHJhbiAqIAogICBEZWx0YVovCiAgIChjKGxsYiwgbGJ2KVsxOmxlbmd0aChsYnYpXV5hbHBoYSAtIGxidiBeIGFscGhhKSAjIGFkZGVkIGV4dHJhIHBhcmVudGhlc2VzCiAKIERlbHRhX25qX291dCA9IHBoaS9sYnYgXiBnYW1tYQogRGVsdGFfbmpfaW4gPSBjKHBoaVsyOmxlbmd0aChwaGkpXSwwKS9jKGxidlsyOmxlbmd0aChwaGkpXSwxKSBeIGdhbW1hCiAjRGVsdGFfbmpfaW4gPSAtYyhwaGlbMjpsZW5ndGgocGhpKV0sMCkvbGJ2IF4gZ2FtbWEKIERlbHRhX25qX25ldCA9IERlbHRhX25qX2luIC0gRGVsdGFfbmpfb3V0ICMgcG9zaXRpdmUgYmVjYXVzZSBvdXQgaXMgbmVnYXRpdmUKICAgCiAgcmV0dXJuKGxpc3QoQ3IgPSBDciwgcGhpID0gcGhpLCBkbmV0ID0gRGVsdGFfbmpfbmV0LCBkaW4gPSAgRGVsdGFfbmpfaW4sIGRvdXQgPSBEZWx0YV9ual9vdXQpKQp9CmBgYAoKCiMjIFNtb290aCBTaHVmZmxlCgpgYGB7cn0KcmVtaW5fc2h1ZmZsZV9zcGVjIDwtIGZ1bmN0aW9uKGFidW5faW4sIC4uLil7CiAgY29yZSA8LSByZW1pbl9zaHVmZmxlKGFidW5faW4sIC4uLikKICBhYnVuX2luICsgY29yZSRkbmV0Cn0KYGBgCgpgYGB7cn0KZGRzX3Rlc3QgPC0gMC45OQp0cnNfc3BlYyA8LSByZW1pbl9zaHVmZmxlX3NwZWModGVzdF9hYnVuX2luLCBkZHNfdGVzdCwgMiwgbGxiID0gLjA4KSAjIC4wNzYsIC4xMDIsIDAuMDgKdHJzX3NwZWMKCihwcmUgPC0gc3VtKENfZiAqIHRlc3RfYWJ1bl9pbiBeIGFnKSkKKGV4cGVjdGVkIDwtIHN1bShDX2YgKiB0ZXN0X2FidW5faW4gXiBhZykgKiBkZHNfdGVzdCkKKG1vZGVsZWQgPC0gc3VtKENfZiAqIHRyc19zcGVjIF4gYWcpKQooZXhwZWN0ZWQgLSBwcmUpL3ByZQoobW9kZWxlZCAtIHByZSkvcHJlCihtb2RlbGVkIC0gZXhwZWN0ZWQpL2V4cGVjdGVkCmBgYAoKbGxiIHNlZW1zIHRvIGJlIHByZXR0eSByZWxldmFudCBmb3IgZ2V0dGluZyBmbHV4IHJpZ2h0IC0tIHByZXN1bWFibHkgSSBhbSBub3QgaGFuZGxpbmcgaXQgY29ycmVjdGx5LiBJJ2QgbGlrZSB0aGluZ3MgdG8gYWRqdXN0IHNvIHRoYXQgaXQgaXMgY29uc2lkZXJlZCBjb3JyZWN0bHkKCmBgYHtyfQp0ZXN0X2FidW5faW4gKiBkZHNfdGVzdApzdW0oKENfZiAqIHRlc3RfYWJ1bl9pbiBeIGFnKSkgKiBkZHNfdGVzdApleHBsb3JlX2xsYiA8LSB0aWJibGUobGxiID0gYygwLCAwLjAxLCAwLjA3LCAwLjA4LCAwLjA5LCAuMSwgLjEwMSwgLjEwMTksIC4xMDE5OSkpICU+JQogIG11dGF0ZShzcGVjID0gbWFwKGxsYiwgfnJlbWluX3NodWZmbGVfc3BlYyh0ZXN0X2FidW5faW4sIGRkc190ZXN0LCAyLCBsbGIgPSAuKSkpCiAgCmV4cGxvcmVfbGxiX3Vud3JhcCA8LSBleHBsb3JlX2xsYiAlPiUKICBtdXRhdGUoc3BlYyA9IG1hcChzcGVjLCB+ZGF0YV9mcmFtZShsYl92ZWMgPSBsYl92ZWMsIHNwZWMgPSAuKSkpICU+JQogIHVubmVzdChzcGVjKSAlPiUKICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gbGJfdmVjLCB2YWx1ZXNfZnJvbSA9IHNwZWMpCgpleHBsb3JlX2xsYl9mbHV4IDwtIGV4cGxvcmVfbGxiICU+JQogIG11dGF0ZShmbHV4ID0gbWFwX2RibChzcGVjLCB+c3VtKENfZiAqIC4gXiBhZykpKSAlPiUKICBzZWxlY3QoLXNwZWMpCgpleHBsb3JlX2xsYl8yIDwtIGxlZnRfam9pbihleHBsb3JlX2xsYl9mbHV4LCBleHBsb3JlX2xsYl91bndyYXApCgogICMKZXhwbG9yZV9sbGJfMgpgYGAKCkFzIHdlIGFwcHJvYWNoIGxsYiA9IGxiX3ZlY1sxXQoKYGBge3J9CmRkc190ZXN0IDwtIDAuOTkKdHJzX3NwZWMgPC0gcmVtaW5fc2h1ZmZsZV9zcGVjKHRlc3RfYWJ1bl9pbiwgZGRzX3Rlc3QsIDIsIGxsYiA9IC4wOCkgIyAuMDc2LCAuMTAyCnRyc19zcGVjCgpzdW0oQ19mICogdGVzdF9hYnVuX2luIF4gYWcpCnN1bShDX2YgKiB0cnNfc3BlYyBeIGFnKQoKYGBgCgoKCgpgYGB7cn0KcmVtaW5fc21vb3RoX3NodWZmbGUgPC0gZnVuY3Rpb24oYWJ1bl9pbiwgREZwY3QsIElwY3QgPSAwLjk5OTksIC4uLil7CiAgIyBERnBjdDogRnJhY3Rpb25hbCBtYXNzIHJldGFpbmVkIGJldHdlZW4gZGVwdGhzCiAgIyBJcGN0OiBGcmFjdGlvbmFsIG1hc3MgcmV0YWluZWQgYmV0d2VlbiBpdGVyYXRpb25zCiAgIyAuLi46IFBhc3NlZCB0byByZW1pbl9zaHVmZmxlCiAgSU1pcnJvciA8LSAyICAtIElwY3QKICAKICBhYnVuX2VzdCA9IGFidW5faW4KICBGcGN0ID0gREZwY3QgIyBnZXRzIG92ZXJ3cml0dGVuIGlmIHdlIGFyZSBpdGVyYXRpbmcKICAKICAjIElmIHdlIGFyZSBsb29zc2luZyBmbHV4LCBhbmQgd2UgbG9vc2UgbG9vc2UgbW9yZSBmbHV4IHRoYW4gSXBjdAogICMgSXRlcmF0ZSByZW1pbiBzaHVmZmxlIG9ubHkga2VlcGluZyBpcGN0IGVhY2ggdGltZQogIGlmKERGcGN0IDwgSXBjdCl7CiAgICBpdGVycyA9IGZsb29yKGxvZyhERnBjdCkvbG9nKElwY3QpKSAjIHdoeSBpcyB0aGlzIGEgcmF0aW8gb2YgbG9nIHRyYW5zZm9ybWVkIHZhbHVlcz8gCiAgICAjIEkgbmVlZCB0byB1bmRlcnN0YW5kIHRoaXMgYmVmb3JlIEkgY2FuIGFkZHJlc3MgdGhlIGNhc2Ugd2hlcmUgREZwY3QgPiBJTWlycm9yCiAgICBpdGVyRmx1eCA9IElwY3ReaXRlcnMKICAgIEZwY3QgPSAxLShpdGVyRmx1eC1ERnBjdCkKICAgIAogICAgZm9yIChpIGluIDE6aXRlcnMpewogICAgICBhYnVuX2VzdCA9IHJlbWluX3NodWZmbGVfc3BlYyhhYnVuX2luID0gYWJ1bl9lc3QsIElwY3QsIC4uLikKICAgIH0KICB9CiAgCiAgIyBJZiB3ZSBhcmUgZ2FpbmluZyBmbHV4LCBhbmQgd2UgZ2FpbiBtb3JlIHRoYW4gSU1pcnJvciwgaXRlcmF0ZQogIGlmKERGcGN0ID4gSU1pcnJvcil7CiAgICBpdGVycyA9IGZsb29yKGxvZyhERnBjdCkvbG9nKElNaXJyb3IpKQogICAgaXRlckZsdXggPSBJTWlycm9yXml0ZXJzCiAgICBGcGN0ID0gMSAtIChpdGVyRmx1eCAtIERGcGN0KQogICAgCiAgICBmb3IgKGkgaW4gMTppdGVycyl7CiAgICAgIGFidW5fZXN0ID0gcmVtaW5fc2h1ZmZsZV9zcGVjKGFidW5faW4gPSBhYnVuX2VzdCwgSU1pcnJvciwgLi4uKQogICAgfQogIH0KICAKICAjIERlYWwgd2l0aCByZW1haW5kZXIuIEluIHRoZSBjYXNlIHdoZXJlIHRoZSBsb3NzIGlzIGxlc3MgdGhhbiBpcGN0LCBvciBncmVhdGVyIHRoYW4gMi1pcGN0IChJbWlycm9yKSwganVzdCBkbyB0aGlzIHBhcnQKICBhYnVuX2VzdCA9IHJlbWluX3NodWZmbGVfc3BlYyhhYnVuX2luID0gYWJ1bl9lc3QsIEZwY3QsIC4uLikKICAKICBhYnVuX2VzdAp9CmBgYAoKYGBge3J9CkRGUCA9IDAuOQp0cnNzMCA8LSByZW1pbl9zaHVmZmxlX3NwZWMoREZwY3QgPSBERlAgLCBhYnVuX2luID0gdGVzdF9hYnVuX2luKSAjIC4wNzYsIC4xMDIKdHJzczAKdHJzcyA8LSByZW1pbl9zbW9vdGhfc2h1ZmZsZShERnBjdCA9ICBERlAsIGFidW5faW4gPSB0ZXN0X2FidW5faW4pICMgLjA3NiwgLjEwMgp0cnNzCgojIENvbmZpcm0gd2hldGhlciBmbHV4IGlzIHRyZWF0ZWQgYXBwcm9wcmlhdGVseQpzdW0oQ19mICogdGVzdF9hYnVuX2luIF4gYWcpICogREZQCgpzdW0oQ19mICogdHJzczAgXiBhZykKCnN1bShDX2YgKiB0cnNzIF4gYWcpCmBgYAoKYGBge3J9CnRpYmJsZSh0ZXN0X2FidW5faW4sIHRyc3MwLCB0cnNzLCBsYl92ZWMpICU+JSBnZ3Bsb3QoYWVzKHggPSBsYl92ZWMpKSArIGdlb21fcG9pbnQoYWVzKHkgPSB0cnNzMCkpICsgZ2VvbV9wb2ludChhZXMoeSA9IHRyc3MpLCBzaGFwZSA9IDIpICsgc2NhbGVfeV9sb2cxMCgpICsgc2NhbGVfeF9sb2cxMCgpICsgZ2VvbV9wb2ludChhZXMoeSA9IHRlc3RfYWJ1bl9pbiksIHNoYXBlID0gMSkKYGBgCgoKCgoKCgojIyMjIEdyYXZleWFyZCBmb3Igbm93CmBgYHtyfQojIEkgd2FudCB0byBtaW5pbWl6ZSB0aGlzIGZ1bmN0aW9uIGhlcmUKc2h1ZmZsZV9jaGVjayA8LSBmdW5jdGlvbihhYnVuX2luLCBERnBjdCwgQ19mMiA9IENfZiwgYWcyID0gYWcsIC4uLil7CiAgYWJ1bl9vdXQgPC0gcmVtaW5fc21vb3RoX3NodWZmbGUoYWJ1bl9pbiwgREZwY3QsIC4uLikKICBmbHV4X2luIDwtIHN1bShDX2YyICogYWJ1bl9pbiBeIGFnMikKICBmbHV4X291dCA8LSBzdW0oQ19mMiAqIGFidW5fb3V0IF4gYWcyKQogIERGUGN0X2FjdHVhbCA9IGZsdXhfb3V0L2ZsdXhfaW4KICBybXNlIDwtIChERnBjdCAtIERGUGN0X2FjdHVhbCleMgogIHJtc2UKfQoKZmluZF9ERlAgPC0gZnVuY3Rpb24oYWJ1bl9pbiwgLi4uKXsKICBhYnVuX2luX2xvYyA9IGFidW5faW4KICAKICBzY193cmFwIDwtIGZ1bmN0aW9uKHgsIGFidW5faW5fbG9jKXsKICAgIHNodWZmbGVfY2hlY2soYWJ1bl9pbiA9IGFidW5faW5fbG9jLCBERnBjdCA9IHgpCiAgfQogIAogIGFkakRmcCA8LSBvcHRpbWl6ZShzY193cmFwLCBjKDAsIDIpLCBhYnVuX2luX2xvYyA9IHRlc3RfYWJ1bl9pbikKICBhZGpEZnAKfQoKCiMgcmVtaW5fc21vb3RoX3NodWZmbGVfZml4IDwtIGZ1bmN0aW9uKGFidW5faW4sIERGcGN0LCAuLi4pewojICAgCiMgfQpgYGAKCgpgYGB7cn0KREZQID0gMC44CnNodWZmbGVfY2hlY2soREZwY3QgPSBERlAgLCBhYnVuX2luID0gdGVzdF9hYnVuX2luKQpmaW5kX0RGUChhYnVuX2luID0gdGVzdF9hYnVuX2luKQoKYGBgCgoKIyMgRGF0YQpGaXJzdCwgbGV0cyBjYWxjdWxhdGUgRGZwY3QgZm9yIGVhY2ggcGFpciBvZiBkZXB0aHMsIGFuZCBsZXRzIGFsc28gc3RhZ2dlciBzcGVjCgoKCmBgYHtyfQpkdW1teV9mdW5jdGlvbiA8LSBmdW5jdGlvbihERnBjdCwgc3BlYywgLi4uKXsKICBERnBjdCAqIHNwZWMKfQpkdW1teV9mdW5jdGlvbigwLjgsIHRlc3RfYWJ1bl9pbikKYGBgCgoKYGBge3J9CnN5bnRoZXRpY19yZW1pbmVyYWxpemVkIDwtIHN5bnRoZXRpY19kYXRhICU+JSAuWy0xLF0gJT4lIG11dGF0ZShwcmVkX3NwZWMgPSBtYXAyKHByZXZfc3BlYywgREZQLCByZW1pbl9zbW9vdGhfc2h1ZmZsZSkpCnN5bnRoZXRpY19yZW1pbmVyYWxpemVkCmBgYAoKRGlkIGl0IHdvcms/CgpgYGB7cn0KdGliYmxlKAphY3R1YWwgPSBzeW50aGV0aWNfcmVtaW5lcmFsaXplZFtbInNwZWNfb25seSJdXVtbNF1dLApwcmVkaWN0ZWQgPSBzeW50aGV0aWNfcmVtaW5lcmFsaXplZFtbInByZWRfc3BlYyJdXVtbNF1dLApwcmV2aW91cyA9IHN5bnRoZXRpY19yZW1pbmVyYWxpemVkW1sicHJldl9zcGVjIl1dW1s0XV0sCmxiID0gbGJfdmVjCikgJT4lIGdhdGhlcihrZXkgPSAidmFyIiwgdmFsdWUgPSAibnAiLCBhY3R1YWwsIHByZWRpY3RlZCwgcHJldmlvdXMpICU+JQogIGdncGxvdChhZXMoeCA9IGxiLCB5ID0gbnAsIHNoYXBlID0gdmFyLCBjb2wgPSB2YXIpLCApICsgZ2VvbV9wb2ludChzaXplID0gMywgYWxwaGEgPSAwLjc1KSArIHNjYWxlX3hfbG9nMTAoKSArIHNjYWxlX3lfbG9nMTAoKSArIGxhYnMoeCA9ICIjIFBhcnRpY2xlcyIsIHkgPSAiU2l6ZSAobW0pIiwgY29sID0gIk1vZGVsIiwgc2hhcGUgPSAiTW9kZWwiKQpgYGAKRnJpZ2dpbmcgZmluYWxseS4gQ29uc2lzdGFudCBldmVyeXRoaW5nIEkgdGhpbmsuCgpMb29rcyBwcm9taXNpbmchIExldHMgYmUgbW9yZSBzeXN0ZW1hdGljIGFib3V0IHRoaXMgbmV4dCEKc3ludGhldGljX3JlbWluZXJhbGl6ZWRfY29uCk9rLiBJZiBJIGNhbGN1bGF0ZSBmbHV4IGFzc3VtaW5nIGFscGhhID0gMi4zIGFuZCBnYW1tYSA9IDEuMywgSSBnZXQgdGhpcyBzdHJvbmdlciB0aGFuIHRyYWRpdGlvbmFsIGFyY2ggZWZmZWN0LgpJIGd1ZXNzIGJlY2F1c2UgZmx1eCBhdHRlbnVhdGVzIGZhc3RlciB0aGFuIGl0IHNob3VsZC4KCklmIEkgc2F5IHRoYXQgYWxwaGEgKyBnYW1tYSA9IDAuNjMgYXMgYmVzdCBmaXQgZmx1eCwgdGhlbiBJIGxvb3NlIG1vcmUgYmlnIHBhcnRpY2xlcyB0aGFuIHNtYWxsIG9uZXMuCk1vcmUgcmVzZXR0aW5nIGluc3RuYWNlcyBhbmQgbm93IHRoaW5ncyBsb29rIHJlYWxseSBzdHJhbmdlLgoKQXMgb2YgYXByaWwgMjEgMjAyMCwgSSdtIHVzaW5nIGFsbGRyZWRnZSBhbHBoYSBhbmQgZ2FtbWEKCkJ1dCBhbHNvIG15IGRpZmZlcmVudCBmbHV4IHByb2ZpbGVzIGFyZSBubyBsb25nZXIgYWRkaW5nIHVwLgoKIyBBcyBhYm92ZSwgYnV0IHRoaXMgdGltZSwgd2l0aCBhZGp1c3RlZCBERlAgdG8gYWNjb3VudCBmb3IgImxlYWthZ2UiCgojIyBMZWFrcwpJJ20gaGF2aW5nIGEgbGVha2FnZSBwcm9ibGVtLiBJIGdldCBuZWFybHksIGJ1dCBub3QgcXVpdGUgdGhlIHJpZ2h0IGFtb3VudCBvZiBmbHV4IG91dC4KV2hhdCBpZiBJIGFkZHJlc3MgdGhpcyBieSBydW5uaW5nIHJlbWluIHNtb290aCBzaHVmZmxlIGFuZCB0aGVuIEkgb3B0aW1pemUgdGhlIERGUGN0IHRoYXQgaXQgZ2V0cyBzbyB0aGF0IGl0IHJldHVybnMgdGhlIHJpZ2h0IGZsdXggb3V0cHV0LgoKSSBvdWdodCB0byBhY3R1YWxseSBmaWd1cmUgb3V0IHdoYXQgaXMgd3JvbmcsIGJ1dCB0aGlzIHNob3VsZCBnaXZlIGEgY2xvc2UgYW5zd2VyIGZvciBtb3N0IGFuYWx5c2VzLgoKT3B0aW1pemF0aW9uIGZ1bmN0aW9uOgpXaGF0IEkgd2FudCBpdCB0byBkby4KVGFrZXMgYSBzdHJ1Y3R1cmUgYW5kIGEgREZQLgpWYXJpZXMgdGhlIERGUCBzZW50IHRvIHJlbWluIHNtb290aCBzaHVmZmxlIHNvIHRoZSBhY3V0YWwgREZQIGxvc3MgaXMgd2hhdCB3ZSB3YW50CgpXZSBuZWVkIGEgZnVuY3Rpb24gd2hlcmUgd2UgY2FuIHBhc3MgdGhlIERGUGN0IHRoYXQgd2UgYXJlIHVzaW5nIGFuZCB0aGUgYWN0dWFsIERGUGN0CmBgYHtyfQpzaHVmZmxlX3R1bmUgPC0gZnVuY3Rpb24oREZwY3RfdG9SZW1pbiwgYWJ1bl9pbiwgIERGcGN0X3RhcmdldCwuLi4pewogIGFidW5fb3V0IDwtIHJlbWluX3Ntb290aF9zaHVmZmxlKGFidW5faW4sIERGcGN0X3RvUmVtaW4sIC4uLikKICBmbHV4X2luIDwtIHN1bShDX2ZfZ2xvYmFsICogYWJ1bl9pbiBeIGFnX2dsb2JhbCkKICBmbHV4X291dCA8LSBzdW0oQ19mX2dsb2JhbCAqIGFidW5fb3V0IF4gYWdfZ2xvYmFsKQogIERGUGN0X2FjdHVhbGx5X2hhcHBlbmVkIDwtIGZsdXhfb3V0L2ZsdXhfaW4KICBybXNlIDwtIChERlBjdF9hY3R1YWxseV9oYXBwZW5lZCAtIERGcGN0X3RhcmdldCleMgogIHJtc2UKfQoKc3RPcHQgPC0gb3B0aW1pemUoc2h1ZmZsZV90dW5lLCBjKDAsIDIpLCBhYnVuX2luID0gdGVzdF9hYnVuX2luLCBERnBjdF90YXJnZXQgPSAwLjkpCnN0T3B0CmBgYAoKU28gZm9yIGEgREZwY3Qgb2YgMC45LCBJIHNob3VsZCBhY3R1YWxseSBmZWVkIGl0IDAuOTE1CgoKCgojIyBBcHBseSBzdG9wdCBvdmVyIHRoZSBkYXRhIHNldC4KCmBgYHtyfQpvcHRGdW4gPC0gZnVuY3Rpb24oYWJ1bl9pbiwgREZwY3QpewogIG9wdCA8LSBvcHRpbWl6ZShzaHVmZmxlX3R1bmUsIGMoMCwgMiksIGFidW5faW4gPSBhYnVuX2luLCBERnBjdF90YXJnZXQgPSBERnBjdCkKICBvcHQkbWluaW11bQp9CmBgYAoKIyMjIFRlc3Qgb3B0aW1pemF0aW9uCgpgYGB7cn0KcGxvdCgKICBzZXEoZnJvbSA9IDAuNSwgdG8gPSAxLCBieSA9IDAuMDUpLAogIG1hcF9kYmwoc2VxKGZyb20gPSAwLjUsIHRvID0gMSwgYnkgPSAwLjA1KSwgfm9wdEZ1bih0ZXN0U3BlY3RydW0sIC4pKQogICkKYGBgCgojIyMgQXBwbHkgb3B0aW1pemF0aW9uCgpgYGB7cn0Kc3ludGhldGljX2RhdGFfZml4RGZwIDwtIHN5bnRoZXRpY19kYXRhICU+JSBmaWx0ZXIoZGVwdGggPiAwKSAlPiUgbXV0YXRlKHVzZV90aGlzX0RGcGN0ID0gbWFwMl9kYmwocHJldl9zcGVjLCBERlAsIG9wdEZ1bikpCmBgYAoKCgpgYGB7cn0Kc3ludGhldGljX2RhdGFfZml4RGZwICU+JSBnZ3Bsb3QoYWVzKHggPSBERlAsIHkgPSB1c2VfdGhpc19ERnBjdCkpICsgZ2VvbV9wb2ludCgpICsgZ2VvbV9hYmxpbmUodiA9IDEsaCA9IDEpCmBgYAoKYGBge3J9CnN5bnRoZXRpY19kYXRhX2ZpeERmcCAlPiUgZ2dwbG90KGFlcyh4ID0gREZQLCB5ID0gdXNlX3RoaXNfREZwY3QgLSBERlApKSArIGdlb21fcG9pbnQoKSArIGdlb21fYWJsaW5lKHYgPSAxLGggPSAxKQpgYGAKCiMgUmVkbyByZW1pbmVyYWxpemF0aW9uCgpgYGB7cn0Kc3ludGhldGljX3JlbWluZXJhbGl6ZWQgPC0gc3ludGhldGljX2RhdGFfZml4RGZwICU+JSAuWy0xLF0gJT4lIG11dGF0ZShwcmVkX3NwZWMgPSBtYXAyKHByZXZfc3BlYywgdXNlX3RoaXNfREZwY3QsIHJlbWluX3Ntb290aF9zaHVmZmxlKSkKc3ludGhldGljX3JlbWluZXJhbGl6ZWQKYGBgCgoKCiMgQW5hbHl6aW5nIHJlbWluZXJhbGl6ZWQgZGF0YQpJbmRpY2F0aW5nIHdoaWNoIHJlZ2lvbnMgYXJlIGFjdHVhbGx5IGxvb3NpbmcgZmx1eCB0aGUgd2hvbGUgdGltZS4KTGV0cyBmaXJzdCBsb29rIGF0IHBhcnRpY2xlIG51bWJlcnMsIG9ic2VydmVkIG1pbnVzIGV4cGVjdGVkCgpgYGB7cn0KZGZwUGx0IDwtIHN5bnRoZXRpY19yZW1pbmVyYWxpemVkICU+JSBnZ3Bsb3QoYWVzKHkgPSBkZXB0aCwgeCA9IERGUCwgY29sID0gREZQIDwgMSkpICsgZ2VvbV9wb2ludCgpICsgc2NhbGVfeV9yZXZlcnNlKCkgCmZsdXhQbHQgPC0gc3ludGhldGljX3JlbWluZXJhbGl6ZWQgJT4lIGdncGxvdChhZXMoeSA9IGRlcHRoLCB4ID0gRmx1eCwgY29sID0gREZQIDwgMSkpICsgZ2VvbV9wb2ludCgpICsgc2NhbGVfeV9yZXZlcnNlKCkKcGxvdF9ncmlkKGRmcFBsdCwgZmx1eFBsdCkKYGBgCgpgYGB7cn0Kc3ludGhldGljX3JlbWluZXJhbGl6ZWRfcHJvYyA8LSBzeW50aGV0aWNfcmVtaW5lcmFsaXplZCAlPiUgbXV0YXRlKFRQID0gbWFwX2RibChzcGVjLCBzdW0pLCBwcmVkX1RQID0gbWFwX2RibChwcmVkX3NwZWMsIHN1bSksIGRpZl9UUCA9IFRQLXByZWRfVFApIApgYGAKCmBgYHtyfQpkdHBQIDwtIHN5bnRoZXRpY19yZW1pbmVyYWxpemVkX3Byb2MgJT4lIGdncGxvdChhZXMoeSA9IGRlcHRoLCB4ID0gZGlmX1RQLCBjb2wgPSBERlAgPCAxKSkgKyBnZW9tX3BvaW50KCkgKyBzY2FsZV95X3JldmVyc2UoKQpwdHBQIDwtIHN5bnRoZXRpY19yZW1pbmVyYWxpemVkX3Byb2MgJT4lIGdncGxvdChhZXMoeSA9IGRlcHRoLCB4ID0gcHJlZF9UUCwgY29sID0gREZQIDwgMSkpICsgZ2VvbV9wb2ludCgpICsgc2NhbGVfeV9yZXZlcnNlKCkgKyBzY2FsZV94X2xvZzEwKCkKdHBQIDwtIHN5bnRoZXRpY19yZW1pbmVyYWxpemVkX3Byb2MgJT4lIGdncGxvdChhZXMoeSA9IGRlcHRoLCB4ID0gVFAsIGNvbCA9IERGUCA8IDEpKSArIGdlb21fcG9pbnQoKSArIHNjYWxlX3lfcmV2ZXJzZSgpICsgc2NhbGVfeF9sb2cxMCgpCnBsb3RfZ3JpZCh0cFAsIHB0cFAsIGR0cFApCmBgYAoKTm93IHRoYXQgSSd2ZSBmaXhlZCB0aGlzIGZvciBwb3NpdGl2ZSB2YWx1ZXMsIHdlIHByZWRpY3QgYWJzdXJkIG51bWJlcnMgb2YgcGFydGljbGVzIG11Y2ggb2YgdGhlIHRpbWUuCkxldHMganVzdCBsb29rIGF0IGRlY3JlYXNpbmcgcGFydHMuCgpgYGB7cn0Kc3ludGhldGljX3JlbWluZXJhbGl6ZWRfcHJvY19kZWNyZWFzaW5nIDwtIHN5bnRoZXRpY19yZW1pbmVyYWxpemVkX3Byb2MgJT4lIGZpbHRlcihERlAgPD0gMSkKYGBgCgpgYGB7cn0KZHRwUCA8LSBzeW50aGV0aWNfcmVtaW5lcmFsaXplZF9wcm9jX2RlY3JlYXNpbmcgJT4lIGdncGxvdChhZXMoeSA9IGRlcHRoLCB4ID0gZGlmX1RQLCBjb2wgPSBERlAgPCAxKSkgKyBnZW9tX3BvaW50KCkgKyBzY2FsZV95X3JldmVyc2UoKQpwdHBQIDwtIHN5bnRoZXRpY19yZW1pbmVyYWxpemVkX3Byb2NfZGVjcmVhc2luZyAlPiUgZ2dwbG90KGFlcyh5ID0gZGVwdGgsIHggPSBwcmVkX1RQLCBjb2wgPSBERlAgPCAxKSkgKyBnZW9tX3BvaW50KCkgKyBzY2FsZV95X3JldmVyc2UoKSArIHNjYWxlX3hfbG9nMTAoKQp0cFAgPC0gc3ludGhldGljX3JlbWluZXJhbGl6ZWRfcHJvY19kZWNyZWFzaW5nICU+JSBnZ3Bsb3QoYWVzKHkgPSBkZXB0aCwgeCA9IFRQLCBjb2wgPSBERlAgPCAxKSkgKyBnZW9tX3BvaW50KCkgKyBzY2FsZV95X3JldmVyc2UoKSArIHNjYWxlX3hfbG9nMTAoKQpmcmFjdHBQIDwtIHN5bnRoZXRpY19yZW1pbmVyYWxpemVkX3Byb2NfZGVjcmVhc2luZyAlPiUgZ2dwbG90KGFlcyh5ID0gZGVwdGgsIHggPSBkaWZfVFAvVFAsIGNvbCA9IERGUCA8IDEpKSArIGdlb21fcG9pbnQoKSArIHNjYWxlX3lfcmV2ZXJzZSgpCnBsb3RfZ3JpZCh0cFAsIHB0cFAsIGR0cFAsIGZyYWN0cFApCmBgYAoKT2ggZGVhci4gVGhlIG1vZGVsIHByZWRpY3RzIHByb2ZvdW5kIHBhcnRpY2xlIG51bWJlciBhdHRlbnVhdGlvbi4gQWN0dWFsbHkgdGhlcmUgaXNuJ3Qgc28gZXZlcnl0aGluZyBhY3R1YWxseSBqdXN0IGZvbGxvd3MgZmx1eC4KCk1heWJlIHRoZXJlJ3Mgc29tZSBiZXR0ZXIgbWV0cmljLiBMaWtlIGZyYWN0aW9uIG9mIGZsdXggb24gcGFydGljbGVzIHNtYWxsZXIgdGhhbiA1MyBtaWNyb24gb3Igc29tZXRoaW5nLgoKCgpUaGlzIG1heSBoYXZlIGJlZW4gYSBkZWFkIGVuZC4gSSBuZWVkIHRvIHJlZmxlY3QgYWdhaW4uCgpgYGB7cn0KdXNlUm93IDwtIHdoaWNoKHN5bnRoZXRpY19yZW1pbmVyYWxpemVkJERGUCA+IDEpWzFdCgp0aWJibGUoCmFjdHVhbCA9IHN5bnRoZXRpY19yZW1pbmVyYWxpemVkW1sic3BlY19vbmx5Il1dW1t1c2VSb3ddXSwKcHJlZGljdGVkID0gc3ludGhldGljX3JlbWluZXJhbGl6ZWRbWyJwcmVkX3NwZWMiXV1bW3VzZVJvd11dLApwcmV2aW91cyA9IHN5bnRoZXRpY19yZW1pbmVyYWxpemVkW1sicHJldl9zcGVjIl1dW1t1c2VSb3ddXSwKbGIgPSBsYl92ZWMKKSAlPiUgZ2F0aGVyKGtleSA9ICJ2YXIiLCB2YWx1ZSA9ICJucCIsIGFjdHVhbCwgcHJlZGljdGVkLCBwcmV2aW91cykgJT4lCiAgZ2dwbG90KGFlcyh4ID0gbGIsIHkgPSBucCwgc2hhcGUgPSB2YXIsIGNvbCA9IHZhciksICkgKyBnZW9tX3BvaW50KHNpemUgPSAzLCBhbHBoYSA9IDAuNzUpICsgc2NhbGVfeF9sb2cxMCgpICsgc2NhbGVfeV9sb2cxMCgpCmBgYApBaC4gU29tZXRoaW5ncyBvZmYgaGVyZS4KCmBgYHtyfQp0ZXN0X2FidW5faW4KdHJzczAwIDwtIHJlbWluX3Ntb290aF9zaHVmZmxlKERGcGN0ID0gIDEsIGFidW5faW4gPSB0ZXN0X2FidW5faW4pICMgLjA3NiwgLjEwMgp0cnNzMDAKCnRyc3MwMCA8LSByZW1pbl9zaHVmZmxlX3NwZWMoREZwY3QgPSAgMS4wMSwgYWJ1bl9pbiA9IHRlc3RfYWJ1bl9pbikgIyAuMDc2LCAuMTAyCnRyc3MwMApgYGAKCiMgMTMgQXByaWwgMjAyMApIbW0uIEJlY2F1c2Ugb2YgdGhlIHBvd2VyIGxhdyBuYXR1cmUgb2YgcGFydGljbGVzIGFuZCBzaXplIGVzc2VudGlhbGx5ICJhbGwiIG9mIHRoZSBwYXJ0aWNsZXMgYXJlIGNyZWF0ZWQgYXQgZWFjaCBkZXB0aC4gRXNzZW50aWFsbHkgcGFydGljbGUgbnVtYmVyIGlzbid0IGEgZ3JlYXQgcHJveGllIGZvciBkaXNhZ2dyZWdhdGlvbi4KCkkgaGF2ZSBhIGNvdXBsZSBvZiBpZGVhcy4KCk9uZSBpcyB0byBsb29rIGF0IDw1MyBtaXJvbiB2cyA+NTMgbWljcm9uIHBhcnRpY2xlcy4gT25lIGNvdWxkIGFzazoKSG93IG11Y2ggYmlvbWFzcyBvciBmbHV4IG1vdmVzIGZyb20gb25lIHNpZGUgb2YgdGhhdCBsaW5lIHRvIHRoZSBvdGhlcj8KCkknZCBhbHNvIGxpa2UgdG8gZ2V0IGF0IHRoaW5ncyBsaWtlIGhvdyBwYXJ0aWNsZSBwcm9kdWN0aW9uIHJlbGF0ZXMgdG8gZmx1eCBhdHRlbnVhdGlvbi4gTGlrZSBob3cgbXVjaCBvZiBmbHV4IGF0dGVudWF0aW9uIGlzIGRpZmZlcmVudCB3aXRoIHRoZSBzbWFsbCBwYXJ0aWNsZXMgdGhhbiBpZiB0aGVyZSB3ZXJlbid0IHRoZXNlIHNtYWxsIHBhcnRpY2xlcy4KCkkgY291bGQgaW1hZ2luZSBjYWxjdWxhdGluZyBDX3IgZXZlcnl3aGVyZSBhbmQgdGhlbiBqdXN0IHJ1bm5pbmcgYSBwdXJlbHkgcHJpc20gbW9kZWwgcHJvcGlnYXRpbmcgZG93bndhcmQgd2l0aCB0aGF0IENyLiBTZWVpbmcgd2hhdCBmbHV4IGlzIGF0IHNvbWUga2V5IHRocmVzaG9sZCwgYW5kIHRoZW4gY29tcGFyaW5nIHRoYXQgdG8gYWN0dWFsIGZsdXguIEknZCBleHBlY3QgdGhhdCBlc3NlbnRpYWxseSBhbGwgb2YgdGhlIGF0dGVudWF0aW9uIHdvdWRsIGJlIHRocm91Z2ggdGhlIHByb2R1Y3Rpb24gYW5kIHJlbW92YWwgb2Ygc21hbGwgcGFydGljbGVzLgoKSW4gdGhlIGJhY2sgb2YgbXkgbWluZCB0aG91Z2gsIG15IGFzc3VtcHRpb25zIGFib3V0IHNtYWxsIHBhcnRpY2xlcyBhcmUgYnVnZ2luZyBtZS4gV2hhdCBpZiB0aGV5IGFyZSBtb3JlIHJlY2FsY2l0cmFudCB0aGFuIGJpZyBwYXJ0aWNsZXMuIFRoZW4gYWxsIG9mIHRoZSBtb2RlbHMgc29ydCBvZiBkb24ndCB3b3JrLCByaWdodD8KCiMgMjAgQXByaWwgMjAyMApTb21lIHVubmVzdGluZwpgYGB7cn0Kc3ludGhldGljX3JlbWluZXJhbGl6ZWRfY29uY2lzZSA8LSBzeW50aGV0aWNfcmVtaW5lcmFsaXplZCAlPiUKICBtdXRhdGUoc3BlYzIgPSBtYXAyKHNwZWMsIHByZXZfc3BlYywgfnRpYmJsZSgueCwgcHJldl9ucCA9IC55KSkpICU+JQogIG11dGF0ZShzcGVjMiA9IG1hcDIoc3BlYzIsIHByZWRfc3BlYywgfnRpYmJsZSgueCwgcHJlZF9ucCA9IC55KSkpICU+JQogIHNlbGVjdCgtYyhzcGVjLCBwcmV2X3NwZWMsIHByZWRfc3BlYywgc3BlY19vbmx5KSkKCnN5bnRoZXRpY19yZW1pbmVyYWxpemVkX3VubmVzdGVkIDwtIHN5bnRoZXRpY19yZW1pbmVyYWxpemVkX2NvbmNpc2UgJT4lIHVubmVzdChzcGVjMikKYGBgCkkgd29uZGVyIHdoYXQgaXMgdXAgd2l0aCB0aGVzZSBuYW1lIHJlYXNzaWdubWVudHM/CgoKR29hbDogRXN0aW1hdGUgYXR0ZW51YXRpb24gb2YgbGFyZ2UgYW5kIHNtYWxsIHBhcnRpY2xlcy4gQ29tcGFyZSB0aGF0IHRvIHRvdGFsIGF0dGVudWF0aW9uLgooSG9wZWZ1bGx5IHRoZXkgc3VtIHRvIHRoZSBzYW1lIHRoaW5nKS4gCkRvIHRoaXMgZm9yIGJvdGggdGhlIG1vZGVsLCBhbmQgZm9yIHRoZSBvYnNlcnZlZCBkYXRhLgoKQ2FsY3VsYXRlIGZsdXggdHJhbnNmZXIgdG8gc21hbGwgcGFydGljbGVzIGZyb20gZGlzYWdncmVnYXRpb24gd2hpY2ggc2hvdWxkIGJlIAoKT2JzZXJ2ZWRTbWFsbEZsdXggLSBQcmVkaWN0ZWRTbWFsbEZsdXgKV2hpY2ggc2hvdWxkIGVxdWFsOiBQcmVkaWN0ZWRCaWdGbHV4IC0gT2JzZXJ2ZWRGbHV4CmBgYHtyfQpzcjAyIDwtIHN5bnRoZXRpY19yZW1pbmVyYWxpemVkX3VubmVzdGVkICU+JQogIG11dGF0ZShwcmV2X2ZsdXggPSBDX2YgKiBwcmV2X25wIF4gYWcsCiAgICAgICAgIHByZWRfZmx1eCA9IENfZiAqIHByZWRfbnAgXiBhZykKCnNyMDJUb3QgPC0gc3IwMiAlPiUKICBncm91cF9ieShkZXB0aCkgJT4lCiAgc3VtbWFyaXplKERGID0gZmlyc3QoREYpLCBERlAgPSBmaXJzdChERlApLAogICAgICAgICAgICBGbHV4ID0gc3VtKGZsdXgpLCBQcmV2X0ZsdXggPSBzdW0ocHJldl9mbHV4KSwgUHJlZF9GbHV4ID0gc3VtKHByZWRfZmx1eCkpCgpzcjAyU21hbGwgPC0gc3IwMiAlPiUgCiAgZmlsdGVyKGxiIDw9IDAuNTMpICU+JQogIGdyb3VwX2J5KGRlcHRoKSAlPiUKICBzdW1tYXJpemUoRmx1eCA9IHN1bShmbHV4KSwgUHJldl9GbHV4ID0gc3VtKHByZXZfZmx1eCksIFByZWRfRmx1eCA9IHN1bShwcmVkX2ZsdXgpKQoKc3IwMkJpZyA8LSBzcjAyICU+JSAKICBmaWx0ZXIobGIgPiAwLjUzKSAlPiUKICBncm91cF9ieShkZXB0aCkgJT4lCiAgc3VtbWFyaXplKEZsdXggPSBzdW0oZmx1eCksIFByZXZfRmx1eCA9IHN1bShwcmV2X2ZsdXgpLCBQcmVkX0ZsdXggPSBzdW0ocHJlZF9mbHV4KSkKCnNyMDJBbGwgPC0gc3IwMlRvdCAlPiUKICBsZWZ0X2pvaW4oc3IwMlNtYWxsLCBieSA9ICJkZXB0aCIsIHN1ZmZpeCA9IGMoIiIsICJfU21hbGwiKSkgJT4lCiAgbGVmdF9qb2luKHNyMDJCaWcsIGJ5ID0gImRlcHRoIiwgc3VmZml4ID0gYygiIiwgIl9CaWciKSkgJT4lCiAgbXV0YXRlKG9zcHMgPSBGbHV4X1NtYWxsIC0gUHJlZF9GbHV4X1NtYWxsLCBvYnBiID0gIFByZWRfRmx1eF9CaWcgLSBGbHV4X0JpZykgIyBBbmQgb2YgY291cnNlIHRoZXkgYXJlIG5vdCBlcXVhbApoZWFkKHNyMDJBbGwpCmBgYAogIApPay4gT3NwcyB+PSBvYnBiLCB3aGljaCBtZWFucyB0aGF0IGJpZyBmbHV4IGxvc3QgYWJvdXQgZXF1YWxzIHNtYWxsIGZsdXggZ2FpbmVkIGR1ZSB0byBkaXNhZ2dyZWdhdGlvbiAKKG9yIHdoYXRldmVyIHByb2NlcyBtYWtlcyB0aGluZ3MgZGV2aWF0ZSBmcm9tIHRoZSBtb2RlbCkKCmBgYHtyfQpzcjAyQWxsICU+JSBmaWx0ZXIoREZQIDw9IDEpICU+JSBnZ3Bsb3QoYWVzKHkgPSBkZXB0aCwgeCA9IG9zcHMsIGNvbCA9IERGUCkpICsgZ2VvbV9wb2ludCgpICsgc2NhbGVfeV9yZXZlcnNlKCkKYGBgCgpIdWguIExvb2sgYXQgdGhhdC4gU29tZXRpbWVzIG9zcHMgZ29lcyBuZWdhdGl2ZSwgd2hpY2ggaXMgdG8gc2F5IHRoZXJlIGlzIGFwcGFyZW50IGFnZ3JlZ2F0aW9uIHRoZXJlLiBJIHRoaW5rLgoKYGBge3J9CnNyMDJBbGwgJT4lIGZpbHRlcihERlAgPD0gMSkgJT4lIGdncGxvdChhZXMoeSA9IERGUCwgeCA9IG9zcHMpKSArIGdlb21fcG9pbnQoKSArIHNjYWxlX3lfcmV2ZXJzZSgpCmBgYAoKT2suIFdoYXQgZG8gSSBhY3R1YWxseSB3YW50IHRvIGtub3cgaGVyZS4gSSB0aGluayBvc3BzIGRldmlkZWQgYnkgdGhlIGZsdXggYXR0ZW51YXRpb24gb2YgdGhlIGJpZyBwYXJ0aWNsZXMgKHByZWRpY3RlZCBvciBvYnNlcnZlZCkuIEkgd2FudCB0byBrbm93IHdoYXQgZnJhY3Rpb24gb2YgYmlnIHBhcnRpY2xlcyBiZWNvbWUgc21hbGwgcGFydGljbGVzIHZzIGp1c3QgZGlzc29sdmUuCgpPciBtYXliZSBzb21ldGhpbmcgZWxzZSB0b28/CgpgYGB7cn0Kc3IwMkFsbCAlPiUgZmlsdGVyKERGUCA8PSAxKSAlPiUgZ2dwbG90KGFlcyh5ID0gREYsIHggPSBvc3BzKSkgKyBnZW9tX3BvaW50KCkgKyBzY2FsZV95X3JldmVyc2UoKQpgYGAKCmBgYHtyfQpvc3BzUGx0IDwtIHNyMDJBbGwgJT4lIGZpbHRlcihERlAgPD0gMSkgJT4lIGdncGxvdChhZXMoeSA9IGRlcHRoLCB4ID0gb3Nwcy9ERikpICsgZ2VvbV9wb2ludCgpICsgc2NhbGVfeV9yZXZlcnNlKCkgKyBsYWJzKHggPSAiRGlzYWdncmVnYXRpb24iKSArIHRoZW1lX2Nvd3Bsb3QoKSArIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDAsIGNvbCA9ICJncmF5NTAiKQpvc3BzUGx0CmBgYApIdWguTG9vayBhdCB0aGlzLiBXZSBoYXZlIGRpc2FnZ3JlZ2F0aW9uIGhhcHBlbmluZyBpbiB0aGUgc3VyZmFjZSwgYXQgfjUwMG0gKGFmdGVyIGFuIHVwdGljayBpbiBmbHV4LCB3aGljaCBnZXRzIGVyYXNlZCwgc28gbWF5YmUgYSB6b29wbGFua3RvbiB0aGluZykgYW5kIGFnYWluIGF0IHRoZSBib3R0b20gb2YgdGhlIE9NWi4gV2UgaGF2ZSBuZXQgYWdncmVnYXRpb24gaW4gdGhlIE9NWiBjb3JlLCBmb3IgdGhlIG1vc3QgcGFydC4KClRoaXMgaXMgY29vbCEKCmBgYHtyfQpwbG90X2dyaWQoZmx1eFBsdCArIGxhYnMoY29sID0gIklzRmx1eERlY3JlYXNpbmc/IikgKyB0aGVtZV9jb3dwbG90KCkgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpLAogICAgICAgICAgb3Nwc1BsdCArIHRoZW1lX2Nvd3Bsb3QoKSkKYGBgCgoKSSB0aGluayB0aGUgc3R1ZmYgYmVsb3cgaXMgbWlzZ3VpZGVkIGFuZCB0aGUgdGhpbmcgYWJvdmUsIGVzcGVjaWFsbHkgY29tYmluZWQgd2lodCBpbGx1c3RyYXRpb25zIG9mIHdoZXJlIGZsdXggaW5jcmVhc2VzLCB3aWxsIGJlIHN1ZmZpY2llbnRseSBpbmZvcm1hdGl2ZS4KCkRpc2FnZ3JlZ2F0aW9uIGFyZSBwb3NpdGl2ZSB2YWx1ZXMgaGVyZSwgYnR3LiBBZ2dyZWdhdGlvbiBhcmUgbmVnYXRpdmUgdmFsdWVzLgoKYGBge3J9CnNyMDJBbGwgJT4lIGZpbHRlcihERlAgPD0gMSkgJT4lIGdncGxvdChhZXMoeSA9IGRlcHRoLCB4ID0gb3Nwcy8oUHJldl9GbHV4X0JpZy0gRmx1eF9CaWcpKSkgKyBnZW9tX3BvaW50KCkgKyBzY2FsZV95X3JldmVyc2UoKSArIHNjYWxlX3hfbG9nMTAoKQpgYGAKCldoeSBpcyB0aGUgYWJvdmUgZXZlciBtb3JlIHRoYW4gb25lPyBNb3JlIHN0dWZmIGRpc2FnZ3JlZ2F0ZXMgdGhhbiBkaXNhcGVhcnM/IElmIGl0IGRpc2FwZWFycywgZG9lc24ndCBpdCBkaXNhZ2dyZWdhdGU/CgpgYGB7cn0KcGxvdF9wcmV2X2JpZyA8LSBzcjAyQWxsICU+JSBmaWx0ZXIoREZQIDw9IDEpICU+JSBnZ3Bsb3QoYWVzKHkgPSBkZXB0aCwgeCA9IFByZXZfRmx1eF9CaWctRmx1eF9CaWcpKSArIGdlb21fcG9pbnQoKSArIHNjYWxlX3lfcmV2ZXJzZSgpIApwbG90X2JpZyA8LSBzcjAyQWxsICU+JSBmaWx0ZXIoREZQIDw9IDEpICU+JSBnZ3Bsb3QoYWVzKHkgPSBkZXB0aCwgeCA9IFByZXZfRmx1eF9CaWcgLSBQcmVkX0ZsdXhfQmlnKSkgKyBnZW9tX3BvaW50KCkgKyBzY2FsZV95X3JldmVyc2UoKSAKcGxvdF9wcmVkX2JpZyA8LSBzcjAyQWxsICU+JSBmaWx0ZXIoREZQIDw9IDEpICU+JSBnZ3Bsb3QoYWVzKHkgPSBkZXB0aCwgeCA9IFByZWRfRmx1eF9CaWcpKSArIGdlb21fcG9pbnQoKSArIHNjYWxlX3lfcmV2ZXJzZSgpICsgc2NhbGVfeF9sb2cxMCgpCgpjb3dwbG90OjpwbG90X2dyaWQocGxvdF9wcmV2X2JpZywgcGxvdF9iaWcsIHBsb3RfcHJlZF9iaWcpCgpgYGAKCkh1aC4gU29tZXRpbWVzIHRoZSBiaWcgZmx1eCBnb2VzIHVwLCBldmVuIHdoZW4gdG90YWwgZmx1eCBnb2VzIGRvd24sIGJvdGggaW4gb2JzZXJ2ZWQgYW5kIHByZWRpY3RlZCBzcGFjZS4gSSBndWVzcyB0aGlzIG1ha2VzIHNlbnNlIGlmIHdlIGhhdmUgYXR0ZW51YXRpb24sIGJ1dCBhIGZsYXR0ZW5pbmcgb2YgdGhlIGN1cnZlLgoKYGBge3J9CnNyMDJBbGwgJT4lIGZpbHRlcihERlAgPD0gMSkgJT4lIGdncGxvdChhZXMoeSA9IGRlcHRoLCB4ID0gb3Nwcy8oUHJldl9GbHV4X0JpZy0gUHJlZF9GbHV4X0JpZykpKSArIGdlb21fcG9pbnQoKSArIHNjYWxlX3lfcmV2ZXJzZSgpICsgc2NhbGVfeF9sb2cxMCgpCmBgYAoKQWxsIHRoaXMgYW5kIG1vcmUgdG9tb3Jyb3cuCgpgYGB7cn0KcGxvdF9ncmlkKGRmcFBsdCwgZmx1eFBsdCkKYGBgCgoKYGBge3J9CnAgPC0gc3IwMkFsbCAlPiUgZmlsdGVyKERGUCA8PSAxKSAlPiUgZ2dwbG90KGFlcyh5ID0gZGVwdGgsIHggPSBvc3BzLyhQcmV2X0ZsdXhfQmlnIC0gRmx1eF9CaWcpKSkgKyBnZW9tX3BvaW50KCkgKyBzY2FsZV95X3JldmVyc2UoKQpwbG90bHk6OmdncGxvdGx5KHApCmBgYAoKODc1IG0gaXMgdGhlIG1vc3QgY2xlYSBleGFtcGxlLiBMZXRzIGxvb2sgYXQgdGhlIHByb2ZpbGVzIGFuZCBzZWUgd2hhdCBpcyBoYXBwZW5pbmcgdGhlcmUuCgp0aWJibGUoCmFjdHVhbCA9IHN5bnRoZXRpY19yZW1pbmVyYWxpemVkW1sic3BlY19vbmx5Il1dW1s0XV0sCnByZWRpY3RlZCA9IHN5bnRoZXRpY19yZW1pbmVyYWxpemVkW1sicHJlZF9zcGVjIl1dW1s0XV0sCnByZXZpb3VzID0gc3ludGhldGljX3JlbWluZXJhbGl6ZWRbWyJwcmV2X3NwZWMiXV1bWzRdXSwKbGIgPSBsYl92ZWMKKSAlPiUgZ2F0aGVyKGtleSA9ICJ2YXIiLCB2YWx1ZSA9ICJucCIsIGFjdHVhbCwgcHJlZGljdGVkLCBwcmV2aW91cykgJT4lCiAgZ2dwbG90KGFlcyh4ID0gbGIsIHkgPSBucCwgc2hhcGUgPSB2YXIsIGNvbCA9IHZhciksICkgKyBnZW9tX3BvaW50KHNpemUgPSAzLCBhbHBoYSA9IDAuNzUpICsgc2NhbGVfeF9sb2cxMCgpICsgc2NhbGVfeV9sb2cxMCgpCgoKYGBge3J9CnN5bnRoZXRpY19yZW1pbmVyYWxpemVkX3VubmVzdGVkICU+JSBmaWx0ZXIoZGVwdGggPT0gODc1LCBsYiA8IDAuMikgJT4lIHNlbGVjdChsYiwgYWN0dWFsID0gbnAsIHByZXZpb3VzID0gcHJldl9ucCwgcHJlZGljdGVkID0gcHJlZF9ucCkgJT4lCmdhdGhlcihrZXkgPSAidmFyIiwgdmFsdWUgPSAibnAiLCBhY3R1YWwsIHByZXZpb3VzLCBwcmVkaWN0ZWQpICU+JQogICBnZ3Bsb3QoYWVzKHggPSBsYiwgeSA9IG5wLCBzaGFwZSA9IHZhciwgY29sID0gdmFyKSwgKSArIGdlb21fcG9pbnQoc2l6ZSA9IDMsIGFscGhhID0gMC43NSkgKyBzY2FsZV94X2xvZzEwKCkgKyBzY2FsZV95X2xvZzEwKCkKYGBgCgpgYGB7cn0Kc3ludGhldGljX3JlbWluZXJhbGl6ZWRfdW5uZXN0ZWQgJT4lIGZpbHRlcihkZXB0aCA9PSA4NzUsIGxiID4gMTApICU+JSBzZWxlY3QobGIsIGFjdHVhbCA9IG5wLCBwcmV2aW91cyA9IHByZXZfbnAsIHByZWRpY3RlZCA9IHByZWRfbnApICU+JQpnYXRoZXIoa2V5ID0gInZhciIsIHZhbHVlID0gIm5wIiwgYWN0dWFsLCBwcmV2aW91cywgcHJlZGljdGVkKSAlPiUKICAgZ2dwbG90KGFlcyh4ID0gbGIsIHkgPSBucCwgc2hhcGUgPSB2YXIsIGNvbCA9IHZhciksICkgKyBnZW9tX3BvaW50KHNpemUgPSAzLCBhbHBoYSA9IDAuNzUpICsgc2NhbGVfeF9sb2cxMCgpICsgc2NhbGVfeV9sb2cxMCgpCmBgYAoKYGBge3J9CnN5bnRoZXRpY19yZW1pbmVyYWxpemVkX3VubmVzdGVkICU+JSBmaWx0ZXIoZGVwdGggPT0gODc1KSAlPiUgbXV0YXRlKHByZXZpb3VzID0gcHJldl9ucCAtIG5wLCBwcmVkaWN0ZWQgPSBwcmVkX25wIC0gbnApICU+JSBzZWxlY3QobGIsIHByZXZpb3VzLCBwcmVkaWN0ZWQpICU+JQpnYXRoZXIoa2V5ID0gInZhciIsIHZhbHVlID0gIm5wIixwcmV2aW91cywgcHJlZGljdGVkKSAlPiUKICAgZ2dwbG90KGFlcyh4ID0gbGIsIHkgPSAxLyhucCksIHNoYXBlID0gdmFyLCBjb2wgPSB2YXIpLCApICsgZ2VvbV9wb2ludChzaXplID0gMywgYWxwaGEgPSAwLjc1KSArIHNjYWxlX3hfbG9nMTAoKQpgYGAKCkh1aC4gVGhlIGN1cnZlIGFwcGVhcnMgdG8gYmUgZmxhdHRlbmluZyBtb3JlIHRoYW4gd2Ugd291bGQgZXhwZWN0IGZyb20gcmFuZG9tIGNoYW5jZS4KQnV0IHRoZXJlIGFyZSBzdGlsbCBtb3JlIHBhcnRpY2xlcyBiZWNhdXNlIG9mIHNoZW5hbmlnYW5zIGluIHRoZSBzbWFsbGlzdCBiaW4uCgpTbyB0aGVzZSBwb3NpdGl2ZSBudW1iZXJzIG1pZ2h0IGluIGZhY3QgYmUgcGxhY2VzIHdpdGggbmVnYXRpdmUgb3NwcyAoYnV0IGFsc28gZmxhdHRlbmluZyBhbmQgc28gYW4gb3Bwb3NpdGUgb2YgdXN1YWwgY2hhbmdlIGluIGxhcmdlIHBhcnRpY2xlcykKCkkgd29uZGVyIGhvdyBldmVuIHRvIGFkZHJlc3MgdGhpcy4KCg==